Skip to content

Conversation

@efriedma-quic
Copy link
Collaborator

0717657 improved constexpr-unkown diagnostics, but potential constant expression checking broke in the process: we produce diagnostics in more cases. Supress the diagnostics as appropriate.

This fix affects -Winvalid-constexpr and the enable_if attribute. (The -Winvalid-constexpr diagnostic isn't really important right now, but it will become important if we allow constexpr-unknown with pre-C++23 standards.)

Fixes #149041. Fixes #149188.

…nown.

0717657 improved constexpr-unkown
diagnostics, but potential constant expression checking broke in the
process: we produce diagnostics in more cases.  Supress the diagnostics
as appropriate.

This fix affects -Winvalid-constexpr and the enable_if attribute.  (The
-Winvalid-constexpr diagnostic isn't really important right now, but it
will become important if we allow constexpr-unknown with pre-C++23
standards.)

Fixes llvm#149041.  Fixes llvm#149188.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 17, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 17, 2025

@llvm/pr-subscribers-clang

Author: Eli Friedman (efriedma-quic)

Changes

0717657 improved constexpr-unkown diagnostics, but potential constant expression checking broke in the process: we produce diagnostics in more cases. Supress the diagnostics as appropriate.

This fix affects -Winvalid-constexpr and the enable_if attribute. (The -Winvalid-constexpr diagnostic isn't really important right now, but it will become important if we allow constexpr-unknown with pre-C++23 standards.)

Fixes #149041. Fixes #149188.


Full diff: https://github.com/llvm/llvm-project/pull/149227.diff

3 Files Affected:

  • (modified) clang/lib/AST/ExprConstant.cpp (+7-4)
  • (modified) clang/test/SemaCXX/constant-expression-p2280r4.cpp (+24)
  • (modified) clang/test/SemaCXX/constexpr-never-constant.cpp (+5)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 767cc4c3b19eb..8797eaddd0e18 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -4450,7 +4450,8 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
         }
       } else if (!IsAccess) {
         return CompleteObject(LVal.getLValueBase(), nullptr, BaseType);
-      } else if (IsConstant && Info.checkingPotentialConstantExpression() &&
+      } else if ((IsConstant || BaseType->isReferenceType()) &&
+                 Info.checkingPotentialConstantExpression() &&
                  BaseType->isLiteralType(Info.Ctx) && !VD->hasDefinition()) {
         // This variable might end up being constexpr. Don't diagnose it yet.
       } else if (IsConstant) {
@@ -4491,9 +4492,11 @@ static CompleteObject findCompleteObject(EvalInfo &Info, const Expr *E,
     // a null BaseVal. Any constexpr-unknown variable seen here is an error:
     // we can't access a constexpr-unknown object.
     if (AK != clang::AK_Dereference && !BaseVal) {
-      Info.FFDiag(E, diag::note_constexpr_access_unknown_variable, 1)
-          << AK << VD;
-      Info.Note(VD->getLocation(), diag::note_declared_at);
+      if (!Info.checkingPotentialConstantExpression()) {
+        Info.FFDiag(E, diag::note_constexpr_access_unknown_variable, 1)
+            << AK << VD;
+        Info.Note(VD->getLocation(), diag::note_declared_at);
+      }
       return CompleteObject();
     }
   } else if (DynamicAllocLValue DA = LVal.Base.dyn_cast<DynamicAllocLValue>()) {
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index 03fea91169787..979d29476c119 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -357,3 +357,27 @@ namespace pointer_comparisons {
   static_assert(!f4()); // expected-error {{static assertion expression is not an integral constant expression}} \
                         // expected-note {{in call to 'f4()'}}
 }
+
+namespace enable_if_1 {
+  template <__SIZE_TYPE__ N>
+  constexpr void foo(const char (&Str)[N])
+  __attribute((enable_if(__builtin_strlen(Str), ""))) {}
+
+  void x() {
+      foo("1234");
+  }
+}
+
+namespace enable_if_2 {
+  constexpr const char (&f())[];
+  extern const char (&Str)[];
+  constexpr int foo()
+  __attribute((enable_if(__builtin_strlen(Str), "")))
+  {return __builtin_strlen(Str);}
+
+  constexpr const char (&f())[] {return "a";}
+  constexpr const char (&Str)[] = f();
+  void x() {
+      constexpr int x = foo();
+  }
+}
diff --git a/clang/test/SemaCXX/constexpr-never-constant.cpp b/clang/test/SemaCXX/constexpr-never-constant.cpp
index 307810ee263dd..ca6dff22275d5 100644
--- a/clang/test/SemaCXX/constexpr-never-constant.cpp
+++ b/clang/test/SemaCXX/constexpr-never-constant.cpp
@@ -24,3 +24,8 @@ constexpr void other_func() {
 
   throw 12;
 }
+
+// Make sure these don't trigger the diagnostic.
+extern const bool& b;
+constexpr bool fun1() { return b; }
+constexpr bool fun2(const bool& b) { return b; }

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (presumably, we want to backport that)

Comment on lines +361 to +383
namespace enable_if_1 {
template <__SIZE_TYPE__ N>
constexpr void foo(const char (&Str)[N])
__attribute((enable_if(__builtin_strlen(Str), ""))) {}

void x() {
foo("1234");
}
}

namespace enable_if_2 {
constexpr const char (&f())[];
extern const char (&Str)[];
constexpr int foo()
__attribute((enable_if(__builtin_strlen(Str), "")))
{return __builtin_strlen(Str);}

constexpr const char (&f())[] {return "a";}
constexpr const char (&Str)[] = f();
void x() {
constexpr int x = foo();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you reference the issue (either a comment or in the namespace name)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We normally use the namespace name, I think we should stick w/ that.

// Make sure these don't trigger the diagnostic.
extern const bool& b;
constexpr bool fun1() { return b; }
constexpr bool fun2(const bool& b) { return b; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you reference the issue (either a comment or in the namespace name)?

@efriedma-quic efriedma-quic merged commit 6a60f18 into llvm:main Jul 17, 2025
8 of 9 checks passed
@github-project-automation github-project-automation bot moved this from Needs Triage to Done in LLVM Release Status Jul 17, 2025
@efriedma-quic
Copy link
Collaborator Author

/cherry-pick 6a60f18

@llvmbot
Copy link
Member

llvmbot commented Jul 17, 2025

Failed to cherry-pick: 6a60f18

https://github.com/llvm/llvm-project/actions/runs/16355033735

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Comment on lines +361 to +383
namespace enable_if_1 {
template <__SIZE_TYPE__ N>
constexpr void foo(const char (&Str)[N])
__attribute((enable_if(__builtin_strlen(Str), ""))) {}

void x() {
foo("1234");
}
}

namespace enable_if_2 {
constexpr const char (&f())[];
extern const char (&Str)[];
constexpr int foo()
__attribute((enable_if(__builtin_strlen(Str), "")))
{return __builtin_strlen(Str);}

constexpr const char (&f())[] {return "a";}
constexpr const char (&Str)[] = f();
void x() {
constexpr int x = foo();
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We normally use the namespace name, I think we should stick w/ that.

@zwuis
Copy link
Contributor

zwuis commented Jul 23, 2025

Can you manually backport this patch? See #150131.

@frederick-vs-ja
Copy link
Contributor

Can you manually backport this patch? See #150131.

This is being backported manually in #149402.

@frederick-vs-ja
Copy link
Contributor

Looks like there're still some other false-positive cases (https://godbolt.org/z/1G9WPn9de):

constexpr bool same_address(const int &a, const int &b) { return &a == &b; }
constexpr int next_element(const int &p) { return (&p)[2]; }

struct Base {};
struct Derived : Base { int n; };
constexpr int get_derived_member(const Base& b) { return static_cast<const Derived&>(b).n; }

struct PolyBase {
  constexpr virtual int get() const { return 0; }
};
struct PolyDerived : PolyBase {
  constexpr int get() const override { return 1; }
};
constexpr int virtual_call(const PolyBase& b) { return b.get(); }

constexpr int arr[3]{0, 1, 2};
static_assert(same_address(arr[1], arr[1]));
static_assert(next_element(arr[0]) == 2);

static_assert(get_derived_member(Derived{}) == 0);
static_assert(virtual_call(PolyDerived{}) == 1);

@efriedma-quic
Copy link
Collaborator Author

And a few more:

constexpr auto& type(const PolyBase& b) { return typeid(b); }

// dynamic_cast
constexpr const void* dyncast(const PolyBase& b) { return dynamic_cast<const void*>(&b); }

constexpr int sub(int (&a)[], int (&b)[]) { return a-b; }

constexpr int* add(int &a) { return &a+10; }

I'll take another look at this... but I think we just have to explicitly suppress the diagnostic in all the relevant cases. Which is a bit of a pain, but I don't see an alternative.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category release:cherry-pick-failed

Projects

Development

Successfully merging this pull request may close these issues.

[Clang] Error on an enable_if attribute in ADT/StringRef.h in C++23 mode [Clang] -Winvalid-constexpr misbehaves in C++23 mode

6 participants