llvm/llvm-project

"this requires expression will only be checked for syntactic validity" suggests bad fixit, is not emitted on equally dangerous code

Open

#47 285 ouverte le 22 oct. 2020

Voir sur GitHub
 (7 commentaires) (0 réactions) (0 assignés)C++ (10 782 forks)batch import
bugzillaclang:diagnosticsconceptsgood first issue

Métriques du dépôt

Stars
 (26 378 stars)
Métriques de merge PR
 (Merge moyen 1j 2h) (1 000 PRs mergées en 30 j)

Description

Bugzilla Link 47941
Version unspecified
OS All
CC @zygoloid

Extended Description

https://godbolt.org/z/hhWdzd

template<class T>
concept Foo = requires {
    { requires { sizeof(T)==0; } };
};
static_assert(Foo<int>);

template<class T>
concept Bar = requires {
    sizeof(T)==0;
};
static_assert(Bar<int>);

template<class T>
concept Baz = requires {
    requires requires { sizeof(T)==0; };
};
static_assert(Baz<int>);

These are all correct C++20 behavior, but it's surprising that the user-programmer can end up here by the usual "add/remove braces until the compiler stops complaining" approach. Start with

    template<class T>
    concept Foo = requires {
        requires { sizeof(T)==0 };
    };

Clang complains:

<source>:3:28: error: expected ';' at end of requirement
    requires { sizeof(T)==0 };
                           ^
                           ;
<source>:3:5: warning: this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression) [-Wrequires-expression]
    requires { sizeof(T)==0 };
    ^
    requires

That is, Clang is suggesting:

    template<class T>
    concept Foo = requires {
        requires requires { sizeof(T)==0; };
    };

(which, to be clear, ALSO does not do what the user intended!)

Or, we can throw braces around the whole thing instead, and Clang will be equally satisfied:

    template<class T>
    concept Foo = requires {
        { requires { sizeof(T)==0; } };
    };

Or, we can "cancel out" the two requires requires Clang suggested, and Clang will be equally satisfied:

    template<class T>
    concept Foo = requires {
        sizeof(T)==0;
    };

Basically, I think Clang should continue to complain about all of these. The only way to shut it up should be the universal signal, "redundant parentheses around the condition."

    template<class T>
    concept Foo = requires {
        ((sizeof(T)==0));              // OK, I guess
        (requires { sizeof(T)==0; });  // OK, I guess
    };

The current diagnostics seem to be in a sour spot of moral hazard, where Clang seems to be TRYING to help the user-programmer, but actually ignores a lot of equally dangerous alternatives.

Is Clang clever enough to detect when the condition is guaranteed to be a constant-expression for any T? e.g. sizeof(T)==0 is a constant-expression regardless of T, because sizeof(T) is integral. When it's a constant expression, I think the likelihood is high that the programmer intended to test truthiness, not well-formedness.

Also notice that sizeof(T)==0 doesn't depend on any requires-expression-parameters; that's an obvious difference between it and e.g. requires(T t) { sizeof(T)==t; } (which arguably is clear enough in its intent to test well-formedness instead of truthiness).

Guide contributeur