"this requires expression will only be checked for syntactic validity" suggests bad fixit, is not emitted on equally dangerous code
#47 285 ouverte le 22 oct. 2020
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
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).