Skip to content

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Feb 23, 2025

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

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
@github-project-automation github-project-automation bot moved this to Needs Triage in LLVM Release Status 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.
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