[clang] 42d49a7 - [clang] Reject constexpr-unknown values as constant expressions more consistently (#129952)

via cfe-commits cfe-commits at lists.llvm.org
Sun Mar 9 18:38:58 PDT 2025


Author: Eli Friedman
Date: 2025-03-09T18:38:55-07:00
New Revision: 42d49a77241df73a17cb442973702fc460e7fb90

URL: https://github.com/llvm/llvm-project/commit/42d49a77241df73a17cb442973702fc460e7fb90
DIFF: https://github.com/llvm/llvm-project/commit/42d49a77241df73a17cb442973702fc460e7fb90.diff

LOG: [clang] Reject constexpr-unknown values as constant expressions more consistently (#129952)

Perform the check for constexpr-unknown values in the same place we
perform checks for other values which don't count as constant
expressions.

While I'm here, also fix a rejects-valid with a reference that doesn't
have an initializer. This diagnostic was also covering up some of the
bugs here.

The existing behavior with -fexperimental-new-constant-interpreter seems
to be correct, but the diagnostics are slightly different; it would be
helpful if someone could check on that as a followup.

Followup to #128409.

Fixes #129844. Fixes #129845.

Added: 
    

Modified: 
    clang/lib/AST/ExprConstant.cpp
    clang/test/CodeGenCXX/cxx23-p2280r4.cpp
    clang/test/SemaCXX/constant-expression-cxx11.cpp
    clang/test/SemaCXX/constant-expression-p2280r4.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d9a1e5bb42343..216bc478bb28b 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -2419,6 +2419,16 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
           LVal.getLValueCallIndex() == 0) &&
          "have call index for global lvalue");
 
+  if (LVal.allowConstexprUnknown()) {
+    if (BaseVD) {
+      Info.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << BaseVD;
+      NoteLValueLocation(Info, Base);
+    } else {
+      Info.FFDiag(Loc);
+    }
+    return false;
+  }
+
   if (Base.is<DynamicAllocLValue>()) {
     Info.FFDiag(Loc, diag::note_constexpr_dynamic_alloc)
         << IsReferenceType << !Designator.Entries.empty();
@@ -3597,7 +3607,8 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
   // expressions here; doing so would regress diagnostics for things like
   // reading from a volatile constexpr variable.
   if ((Info.getLangOpts().CPlusPlus && !VD->hasConstantInitialization() &&
-       VD->mightBeUsableInConstantExpressions(Info.Ctx)) ||
+       VD->mightBeUsableInConstantExpressions(Info.Ctx) &&
+       !AllowConstexprUnknown) ||
       ((Info.getLangOpts().CPlusPlus || Info.getLangOpts().OpenCL) &&
        !Info.getLangOpts().CPlusPlus11 && !VD->hasICEInitializer(Info.Ctx))) {
     if (Init) {
@@ -16993,18 +17004,6 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
 
     if (!Info.discardCleanups())
       llvm_unreachable("Unhandled cleanup; missing full expression marker?");
-
-    if (Value.allowConstexprUnknown()) {
-      assert(Value.isLValue() && "Expected an lvalue");
-      auto Base = Value.getLValueBase();
-      const auto *NewVD = Base.dyn_cast<const ValueDecl *>();
-      if (!NewVD)
-        NewVD = VD;
-      Info.FFDiag(getExprLoc(), diag::note_constexpr_var_init_non_constant, 1)
-          << NewVD;
-      NoteLValueLocation(Info, Base);
-      return false;
-    }
   }
 
   return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,

diff  --git a/clang/test/CodeGenCXX/cxx23-p2280r4.cpp b/clang/test/CodeGenCXX/cxx23-p2280r4.cpp
index d5409be451df0..b62c68c66f0fa 100644
--- a/clang/test/CodeGenCXX/cxx23-p2280r4.cpp
+++ b/clang/test/CodeGenCXX/cxx23-p2280r4.cpp
@@ -4,7 +4,7 @@
 
 extern int& s;
 
-// CHECK: @_Z4testv()
+// CHECK-LABEL: @_Z4testv()
 // CHECK-NEXT: entry:
 // CHECK-NEXT: [[I:%.*]] = alloca ptr, align {{.*}}
 // CHECK-NEXT: [[X:%.*]] = load ptr, ptr @s, align {{.*}}
@@ -13,3 +13,16 @@ int& test() {
   auto &i = s;
   return i;
 }
+
+// CHECK-LABEL: @_Z1fv(
+// CHECK: [[X1:%.*]] = load ptr, ptr @x, align 8
+// CHECK-NEXT: store ptr [[X1]]
+// CHECK: [[X2:%.*]] = load ptr, ptr @x, align 8
+// CHECK-NEXT: store ptr [[X2]]
+// CHECK: [[X3:%.*]] = load ptr, ptr @x, align 8
+// CHECK-NEXT: store ptr [[X3]]
+int &ff();
+int &x = ff();
+struct A { int& x; };
+struct B { A x[20]; };
+B f() { return {x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x}; }

diff  --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp
index 4a7f4fd9c7e50..0fb072709b051 100644
--- a/clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1472,8 +1472,8 @@ namespace ConvertedConstantExpr {
   enum class E {
     em = m,
     en = n, // expected-error {{enumerator value is not a constant expression}} cxx11_20-note {{initializer of 'n' is unknown}}
-    eo = (m + // pre-cxx23-error {{not a constant expression}}
-          n // cxx11_20-note {{initializer of 'n' is unknown}} cxx23-error {{not a constant expression}}
+    eo = (m + // expected-error {{not a constant expression}}
+          n // cxx11_20-note {{initializer of 'n' is unknown}}
           ),
     eq = reinterpret_cast<long>((int*)0) // expected-error {{not a constant expression}} expected-note {{reinterpret_cast}}
   };

diff  --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index 65e5e6b34b48f..f22430a0e88a2 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++23 -verify %s
+// RUN: %clang_cc1 -std=c++23 -verify=expected,nointerpreter %s
 // RUN: %clang_cc1 -std=c++23 -verify %s -fexperimental-new-constant-interpreter
 
 using size_t = decltype(sizeof(0));
@@ -39,8 +39,8 @@ void splash(Swim& swam) {
   static_assert(swam.phelps() == 28);     // ok
   static_assert((&swam)->phelps() == 28); // ok
   Swim* pswam = &swam;                    // expected-note {{declared here}}
-  static_assert(pswam->phelps() == 28);   // expected-error {{static assertion expression is not an integral constant expression}}
-                                          // expected-note at -1 {{read of non-constexpr variable 'pswam' is not allowed in a constant expression}}
+  static_assert(pswam->phelps() == 28);   // expected-error {{static assertion expression is not an integral constant expression}} \
+                                          // expected-note {{read of non-constexpr variable 'pswam' is not allowed in a constant expression}}
   static_assert(how_many(swam) == 28);    // ok
   static_assert(Swim().lochte() == 12);   // ok
   static_assert(swam.lochte() == 12);     // expected-error {{static assertion expression is not an integral constant expression}}
@@ -154,3 +154,26 @@ int g() {
     static_assert(f(arr) == 5);
 }
 }
+
+namespace GH128409 {
+  int &ff();
+  int &x = ff(); // nointerpreter-note {{declared here}}
+  constinit int &z = x; // expected-error {{variable does not have a constant initializer}} \
+                        // expected-note {{required by 'constinit' specifier here}} \
+                        // nointerpreter-note {{initializer of 'x' is not a constant expression}}
+}
+
+namespace GH129845 {
+  int &ff();
+  int &x = ff(); // nointerpreter-note {{declared here}}
+  struct A { int& x; };
+  constexpr A g = {x}; // expected-error {{constexpr variable 'g' must be initialized by a constant expression}} \
+                       // nointerpreter-note {{initializer of 'x' is not a constant expression}}
+  const A* gg = &g;
+}
+
+namespace extern_reference_used_as_unknown {
+  extern int &x;
+  int y;
+  constinit int& g = (x,y); // expected-warning {{left operand of comma operator has no effect}}
+}


        


More information about the cfe-commits mailing list