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 opened on 2020年10月22日

GitHub で見る
 (7 comments) (0 reactions) (0 assignees)C++ (10,782 forks)batch import
bugzillaclang:diagnosticsconceptsgood first issue

Repository metrics

Stars
 (26,378 stars)
PR merge metrics
 (平均マージ 1d 2h) (30d で 1,000 merged PRs)

説明

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

コントリビューターガイド