[Concepts] Make constraint expressions unevaluated until satisfaction checking
authorSaar Raz <saar@raz.email>
Fri, 24 Jan 2020 00:22:17 +0000 (02:22 +0200)
committerSaar Raz <saar@raz.email>
Fri, 24 Jan 2020 00:24:21 +0000 (02:24 +0200)
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.

clang/lib/Parse/ParseExpr.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp
clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp [new file with mode: 0644]

index ba7525ecf527455d2b03703347294955ff320036..e0c53df992e803bb065e6807ae3adebb8ab50b98 100644 (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,
index 92f6e0dc1c909c5abe68754a26f1989fefb2841f..fbbab8f0070392e9fbef97e21aa93a171933aa1c 100755 (executable)
@@ -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;
index bc093a0fc50e19c685055ce35ab16438b1523f8b..b45b57f6b9242147ce3d362d5fa4a9e1409a27bd 100644 (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}}
 }
\ No newline at end of file
diff --git a/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp b/clang/test/SemaTemplate/cxx2a-constraint-exprs.cpp
new file mode 100644 (file)
index 0000000..a2a7232
--- /dev/null
@@ -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}}
\ No newline at end of file