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 aberto em 22 de out. de 2020

Ver no GitHub
 (7 comments) (0 reactions) (0 assignees)C++ (10.782 forks)batch import
bugzillaclang:diagnosticsconceptsgood first issue

Métricas do repositório

Stars
 (26.378 stars)
Métricas de merge de PR
 (Mesclagem média 1d 2h) (1.000 fundiu PRs em 30d)

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).

Guia do colaborador