Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] Treat constexpr-unknown value as invalid in EvaluateAsInitializer #128409

Merged
merged 6 commits into from
Mar 5, 2025

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Feb 23, 2025

It is an alternative to #127525.
Close #127475.

@@ -3628,8 +3628,6 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
if (AllowConstexprUnknown) {
if (!Result)
Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
else
Result->setConstexprUnknown();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Deleting this makes a lot of sense; I was really confused trying to parse what this line in the original patch was supposed to do.

const auto *NewVD = Base.dyn_cast<const ValueDecl *>();
if (!NewVD)
NewVD = VD;
Info.FFDiag(getExprLoc(), diag::note_constexpr_var_init_non_constant, 1)
Copy link
Member Author

Choose a reason for hiding this comment

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

Ideally, we should emit a note here:

if (AllowConstexprUnknown) {
Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
return true;
}

But I don't know how to add a "lazy" note (i.e., these notes will be shown if an error occurs).

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure what you mean by a "lazy" note; the whole point of constexpr-unknown is that it's not an error at the point it's constructed.

If you need more information to generate a good diagnostic, you could pass that information to createConstexprUnknownAPValues, and store it in the APValue itself, I guess. Computing the whole diagnostic in advance seems like it would be too expensive, though.

I don't think it's a big deal if we generate slightly different diagnostics depending on whether we're in C++23 mode, as long as we generate some explanation for what's happening.

Copy link
Member Author

Choose a reason for hiding this comment

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

If you need more information to generate a good diagnostic, you could pass that information to createConstexprUnknownAPValues, and store it in the APValue itself, I guess. Computing the whole diagnostic in advance seems like it would be too expensive, though.

That is what the current implementation does.

@dtcxzyw dtcxzyw changed the title [Clang] Treat constexpr-unknown value as invalid in evaluateValue [Clang] Treat constexpr-unknown value as invalid in EvaluateAsInitializer Feb 26, 2025
@dtcxzyw dtcxzyw marked this pull request as ready for review February 26, 2025 16:12
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. labels Feb 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 26, 2025

@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Yingwei Zheng (dtcxzyw)

Changes

It is an alternative to #127525.
Close #127475.


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

3 Files Affected:

  • (modified) clang/lib/AST/ExprConstant.cpp (+12-2)
  • (modified) clang/lib/CodeGen/CGExprConstant.cpp (+5-1)
  • (added) clang/test/CodeGenCXX/cxx23-p2280r4.cpp (+15)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 6ccb6e23f8d2f..42801dc4e18fa 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -3628,8 +3628,6 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E,
   if (AllowConstexprUnknown) {
     if (!Result)
       Result = &Info.CurrentCall->createConstexprUnknownAPValues(VD, Base);
-    else
-      Result->setConstexprUnknown();
   }
   return true;
 }
@@ -17000,6 +16998,18 @@ 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/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index ee5874b26f534..0295f703b9061 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1883,8 +1883,12 @@ llvm::Constant *ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &D) {
 
   // Try to emit the initializer.  Note that this can allow some things that
   // are not allowed by tryEmitPrivateForMemory alone.
-  if (APValue *value = D.evaluateValue())
+  // Bail out on constexpr-unknown values since they are invalid in CodeGen.
+  if (APValue *value = D.evaluateValue()) {
+    assert(!value->allowConstexprUnknown() &&
+           "Constexpr unknown values are not allowed in CodeGen");
     return tryEmitPrivateForMemory(*value, destType);
+  }
 
   return nullptr;
 }
diff --git a/clang/test/CodeGenCXX/cxx23-p2280r4.cpp b/clang/test/CodeGenCXX/cxx23-p2280r4.cpp
new file mode 100644
index 0000000000000..d5409be451df0
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx23-p2280r4.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++23 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++17 %s -emit-llvm -o - | FileCheck %s
+
+extern int& s;
+
+// CHECK: @_Z4testv()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: [[I:%.*]] = alloca ptr, align {{.*}}
+// CHECK-NEXT: [[X:%.*]] = load ptr, ptr @s, align {{.*}}
+// CHECK-NEXT: store ptr [[X]], ptr [[I]], align {{.*}}
+int& test() {
+  auto &i = s;
+  return i;
+}

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

Should the other Expr::Evaluate* entry points have the same bailout? Do you want to do that in this patch, or a followup?

const auto *NewVD = Base.dyn_cast<const ValueDecl *>();
if (!NewVD)
NewVD = VD;
Info.FFDiag(getExprLoc(), diag::note_constexpr_var_init_non_constant, 1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure what you mean by a "lazy" note; the whole point of constexpr-unknown is that it's not an error at the point it's constructed.

If you need more information to generate a good diagnostic, you could pass that information to createConstexprUnknownAPValues, and store it in the APValue itself, I guess. Computing the whole diagnostic in advance seems like it would be too expensive, though.

I don't think it's a big deal if we generate slightly different diagnostics depending on whether we're in C++23 mode, as long as we generate some explanation for what's happening.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Feb 27, 2025

Should the other Expr::Evaluate* entry points have the same bailout? Do you want to do that in this patch, or a followup?

Sorry I don't have enough bandwidth to work on other potential constexpr-unknown-related issues. I just want to close #127475 as soon as possible.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

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

LGTM

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.

I get that we are allowing constexpr unknown values into codegen and that is a mistake but I don't totally follow the fix. Can you go into more details?

If I am reading the code correctly there should be some constexpr cases that generate a diagnostic, can we verify that with a test?

@efriedma-quic
Copy link
Collaborator

Here's a testcase that's should generate a diagnostic, but doesn't without this patch:

int &ff(); int &x = ff(); constinit int &z = x;

Some related testcases are a little weird... consider the following:

extern int &x; int &z = x;

There's a gap between checking for constant expressions, and codegen, due to the following diagnostic; it triggers in constexpr, but allows evaluating for the value because a CCEDiag doesn't stop the evaluation:

.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Mar 1, 2025

If I am reading the code correctly there should be some constexpr cases that generate a diagnostic, can we verify that with a test?

It has been tested by the following case:

extern int &Recurse1;
int &Recurse2 = Recurse1;
int &Recurse1 = Recurse2;
constexpr int &Recurse3 = Recurse2;

Without diagnostic-related code in this patch, we will lose some notes on Recurse2.

@Mishura4
Copy link

Mishura4 commented Mar 4, 2025

So it looks like 20.1.0 is planned to release tomorrow as there are "no critical issues"... https://discourse.llvm.org/t/20-1-0-release-update-releasing-1-week-early-tomorrow/84932
I'm not an LLVM maintainer so I don't know if it's really my place to say this, but I think you're about to see a lot more GitHub issues filed related to this 😭 I don't think extern references are that uncommon

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.

I am happy to see a fix here but I would still like some more context on whether we think this still leave some cases uncaught or not.

@dtcxzyw dtcxzyw merged commit 27757fb into llvm:main Mar 5, 2025
11 checks passed
@dtcxzyw dtcxzyw deleted the p2280r4-reject-constexpr-unknown branch March 5, 2025 06:01
@frederick-vs-ja frederick-vs-ja added this to the LLVM 20.X Release milestone Mar 5, 2025
@frederick-vs-ja
Copy link
Contributor

/cherry-pick 27757fb

@llvmbot
Copy link
Member

llvmbot commented Mar 5, 2025

/pull-request #129836

@efriedma-quic
Copy link
Collaborator

Opened #129844 with a case that still fails. Other entry-points to constant evaluation need similar treatment to EvaluateAsInitializer.

@efriedma-quic
Copy link
Collaborator

And the check for allowConstexprUnknown() isn't catching all the relevant cases; opened #129845.

efriedma-quic added a commit to efriedma-quic/llvm-project that referenced this pull request Mar 5, 2025
…consistently.

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 llvm#128409.

Fixes llvm#129844. Fixes llvm#129845.
efriedma-quic added a commit that referenced this pull request Mar 10, 2025
…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.
efriedma-quic pushed a commit to efriedma-quic/llvm-project that referenced this pull request Mar 10, 2025
efriedma-quic added a commit to efriedma-quic/llvm-project that referenced this pull request Mar 10, 2025
…consistently (llvm#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 llvm#128409.

Fixes llvm#129844. Fixes llvm#129845.
swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Mar 11, 2025
swift-ci pushed a commit to swiftlang/llvm-project that referenced this pull request Mar 11, 2025
…consistently (llvm#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 llvm#128409.

Fixes llvm#129844. Fixes llvm#129845.
jph-13 pushed a commit to jph-13/llvm-project that referenced this pull request Mar 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
Development

Successfully merging this pull request may close these issues.

[Clang] [CodeGen] Wrong code generation for extern reference
7 participants