leoetlino created this revision.
leoetlino added a reviewer: rsmith.
Herald added a subscriber: kristof.beyls.
leoetlino requested review of this revision.
Herald added a project: clang.

If any arguments of a consteval function call are value-dependent,
the call cannot be evaluated until instantiation.

This patch fixes Sema::CheckForImmediateInvocation so we don't attempt
to evaluate consteval function calls too early (before instantiation).

This fixes things like:

  consteval int f(int n) { return n; }
  
  template <int M>
  constexpr int broken() {
    return f(M);
  }

Without the value-dependency checks, what happens is that the constant
expression evaluation engine is called on the following expression:

  ConstantExpr 'int'
  `-CallExpr 'int'
    |-ImplicitCastExpr 'int (*)(int)' <FunctionToPointerDecay>
    | `-DeclRefExpr 'int (int)' lvalue Function 'f' 'int (int)'
    `-DeclRefExpr 'int' NonTypeTemplateParm 'M' 'int'

which obviously fails when it tries to evaluate M.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D113859

Files:
  clang/lib/Sema/SemaExpr.cpp
  clang/test/SemaCXX/cxx2a-consteval.cpp


Index: clang/test/SemaCXX/cxx2a-consteval.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-consteval.cpp
+++ clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -61,6 +61,20 @@
 struct E : C {
   consteval ~E() {} // expected-error {{cannot be declared consteval}}
 };
+
+template <int X>
+constexpr int callConstevalWithNameDependentArg() {
+  return f1(X);
+}
+
+template <int X>
+constexpr int callConstevalWithNameDependentArgAsVariable() {
+  constexpr int x = X;
+  return f1(x);
+}
+
+auto foo = callConstevalWithNameDependentArg<42>();
+
 }
 
 consteval int main() { // expected-error {{'main' is not allowed to be 
declared consteval}}
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -16654,11 +16654,22 @@
   /// It's OK if this fails; we'll also remove this in
   /// HandleImmediateInvocations, but catching it here allows us to avoid
   /// walking the AST looking for it in simple cases.
-  if (auto *Call = dyn_cast<CallExpr>(E.get()->IgnoreImplicit()))
+  if (auto *Call = dyn_cast<CallExpr>(E.get()->IgnoreImplicit())) {
     if (auto *DeclRef =
             dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()))
       ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef);
 
+    // If any arguments are value-dependent, we will not be able to evaluate
+    // the function call until instantiation.
+    if (Call->isValueDependent())
+      return E;
+
+    for (Expr *Arg : Call->arguments()) {
+      if (Arg->isValueDependent())
+        return E;
+    }
+  }
+
   E = MaybeCreateExprWithCleanups(E);
 
   ConstantExpr *Res = ConstantExpr::Create(


Index: clang/test/SemaCXX/cxx2a-consteval.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-consteval.cpp
+++ clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -61,6 +61,20 @@
 struct E : C {
   consteval ~E() {} // expected-error {{cannot be declared consteval}}
 };
+
+template <int X>
+constexpr int callConstevalWithNameDependentArg() {
+  return f1(X);
+}
+
+template <int X>
+constexpr int callConstevalWithNameDependentArgAsVariable() {
+  constexpr int x = X;
+  return f1(x);
+}
+
+auto foo = callConstevalWithNameDependentArg<42>();
+
 }
 
 consteval int main() { // expected-error {{'main' is not allowed to be declared consteval}}
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -16654,11 +16654,22 @@
   /// It's OK if this fails; we'll also remove this in
   /// HandleImmediateInvocations, but catching it here allows us to avoid
   /// walking the AST looking for it in simple cases.
-  if (auto *Call = dyn_cast<CallExpr>(E.get()->IgnoreImplicit()))
+  if (auto *Call = dyn_cast<CallExpr>(E.get()->IgnoreImplicit())) {
     if (auto *DeclRef =
             dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()))
       ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef);
 
+    // If any arguments are value-dependent, we will not be able to evaluate
+    // the function call until instantiation.
+    if (Call->isValueDependent())
+      return E;
+
+    for (Expr *Arg : Call->arguments()) {
+      if (Arg->isValueDependent())
+        return E;
+    }
+  }
+
   E = MaybeCreateExprWithCleanups(E);
 
   ConstantExpr *Res = ConstantExpr::Create(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to