[Concepts] Make constraint expressions unevaluated until satisfaction checking

As per P1980R0, constraint expressions are unevaluated operands, and their constituent atomic
constraints only become constant evaluated during satisfaction checking.

Change the evaluation context during parsing and instantiation of constraints to unevaluated.
This commit is contained in:
Saar Raz 2020-01-24 02:22:17 +02:00
parent 30179d7ecf
commit 73eaf62463
4 changed files with 25 additions and 9 deletions

View File

@ -234,7 +234,7 @@ ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) {
/// \endverbatim
ExprResult Parser::ParseConstraintExpression() {
EnterExpressionEvaluationContext ConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
Actions, Sema::ExpressionEvaluationContext::Unevaluated);
ExprResult LHS(ParseCastExpression(AnyCastExpr));
ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr));
if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) {
@ -256,7 +256,7 @@ ExprResult Parser::ParseConstraintExpression() {
ExprResult
Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) {
EnterExpressionEvaluationContext ConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
Actions, Sema::ExpressionEvaluationContext::Unevaluated);
bool NotPrimaryExpression = false;
auto ParsePrimary = [&] () {
ExprResult E = ParseCastExpression(PrimaryExprOnly,

View File

@ -1849,7 +1849,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
if (TrailingRequiresClause) {
EnterExpressionEvaluationContext ConstantEvaluated(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
TemplateArgs);
if (SubstRC.isInvalid())
@ -2189,7 +2189,7 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl(
Expr *TrailingRequiresClause = D->getTrailingRequiresClause();
if (TrailingRequiresClause) {
EnterExpressionEvaluationContext ConstantEvaluated(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause,
TemplateArgs);
if (SubstRC.isInvalid())
@ -2529,8 +2529,6 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
TemplateArgumentListInfo InstArgs;
if (TemplArgInfo) {
EnterExpressionEvaluationContext ConstantEvaluated(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc);
InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc);
if (SemaRef.Subst(TemplArgInfo->getTemplateArgs(),
@ -3736,7 +3734,7 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {
Expr *InstRequiresClause = nullptr;
if (Expr *E = L->getRequiresClause()) {
EnterExpressionEvaluationContext ConstantEvaluated(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
SemaRef, Sema::ExpressionEvaluationContext::Unevaluated);
ExprResult Res = SemaRef.SubstExpr(E, TemplateArgs);
if (Res.isInvalid() || !Res.isUsable()) {
return nullptr;

View File

@ -39,8 +39,9 @@ namespace std_example {
using dc1 = D_check<short>; // expected-error{{constraints not satisfied for class template 'D_check' [with T = short]}}
template<typename T>
concept C2 = requires (T a) { // expected-note{{'a' declared here}}
concept C2 = requires (T a) {
requires sizeof(a) == 4; // OK
requires a == 0; // expected-error{{constraint variable 'a' cannot be used in an evaluated context}}
requires a == 0; // expected-note{{because 'a == 0' would be invalid: constraint variable 'a' cannot be used in an evaluated context}}
};
static_assert(C2<int>); // expected-note{{because 'int' does not satisfy 'C2'}} expected-error{{static_assert failed}}
}

View File

@ -0,0 +1,17 @@
// RUN: %clang_cc1 -std=c++2a -verify %s
// Make sure constraint expressions are unevaluated before being substituted
// into during satisfaction checking.
template<typename T> constexpr int f() { return T::value; }
template<typename T> concept Foo = false && (f<int>(), true);
bool k = Foo<int>;
template<typename T> requires false && (f<int>(), true) struct S {};
// expected-note@-1{{because}}
using s = S<int>; // expected-error {{constraints not satisfied}}
template<typename T> void foo() requires false && (f<int>(), true) { };
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
int a = (foo<int>(), 0); // expected-error{{no matching function}}
template<typename T> void bar() requires requires { requires false && (f<int>(), true); } { };
// expected-note@-1{{because}} expected-note@-1{{candidate template ignored}}
int b = (bar<int>(), 0); // expected-error{{no matching function}}