cor3ntin updated this revision to Diff 540902.
cor3ntin added a comment.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Add more tests for aggregates and a codegen test


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D155175/new/

https://reviews.llvm.org/D155175

Files:
  clang/include/clang/Sema/Sema.h
  clang/lib/CodeGen/CGExpr.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp
  clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
  clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
  llvm/cmake/modules/HandleLLVMOptions.cmake

Index: llvm/cmake/modules/HandleLLVMOptions.cmake
===================================================================
--- llvm/cmake/modules/HandleLLVMOptions.cmake
+++ llvm/cmake/modules/HandleLLVMOptions.cmake
@@ -610,7 +610,7 @@
   # crash if LLVM is built with GCC and LTO enabled (#57740).  Until
   # these bugs are fixed, we need to disable dead store eliminations
   # based on object lifetime.
-  add_flag_if_supported("-fno-lifetime-dse" CMAKE_CXX_FLAGS)
+  # add_flag_if_supported("-fno-lifetime-dse" CMAKE_CXX_FLAGS)
 endif ( LLVM_COMPILER_IS_GCC_COMPATIBLE )
 
 # Modules enablement for GCC-compatible compilers:
Index: clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
===================================================================
--- clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
+++ clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
@@ -164,3 +164,164 @@
 int i = g(x); // expected-error {{call to immediate function 'ConstevalConstructor::g<int>' is not a constant expression}} \
               // expected-note {{read of non-const variable 'x' is not allowed in a constant expression}}
 }
+
+
+
+namespace Aggregate {
+consteval int f(int); // expected-note {{declared here}}
+struct S {
+  int x = f(42); // expected-note {{undefined function 'f' cannot be used in a constant expression}}
+};
+
+constexpr S immediate(auto) {
+    return S{};
+}
+
+void test_runtime() {
+    (void)immediate(0); // expected-error {{call to immediate function 'Aggregate::immediate<int>' is not a constant expression}} \
+                        // expected-note {{in call to 'immediate(0)'}}
+}
+consteval int f(int i) {
+    return i;
+}
+consteval void test() {
+    constexpr S s = immediate(0);
+    static_assert(s.x == 42);
+}
+}
+
+
+
+namespace GH63742 {
+void side_effect(); // expected-note  {{declared here}}
+consteval int f(int x) {
+    if (!x) side_effect(); // expected-note {{non-constexpr function 'side_effect' cannot be used in a constant expression}}
+    return x;
+}
+struct SS {
+  int x = f(0); // expected-error {{call to consteval function 'GH63742::f' is not a constant expression}} \
+                // expected-note  {{declared here}} \
+                // expected-note  {{in call to 'f(0)'}}
+  SS();
+};
+SS::SS(){} // expected-note {{in the default initializer of 'x'}}
+
+consteval int f2(int x) {
+    if (!__builtin_is_constant_evaluated()) side_effect();
+    return x;
+}
+struct S2 {
+    int x = f2(0);
+    constexpr S2();
+};
+
+constexpr S2::S2(){}
+S2 s = {};
+
+struct S3 {
+    int x = f2(0);
+    S3();
+};
+S3::S3(){}
+
+}
+
+namespace Defaulted {
+consteval int f(int x);
+struct SS {
+  int x = f(0);
+  SS() = default;
+};
+}
+
+namespace DefaultedUse{
+consteval int f(int x);  // expected-note {{declared here}}
+struct SS {
+  int x = f(0); // expected-note {{undefined function 'f' cannot be used in a constant expression}}
+  SS() = default;
+};
+
+void test() {
+    [[maybe_unused]] SS s; // expected-error {{call to immediate function 'DefaultedUse::SS::SS' is not a constant expression}} \
+                           //  expected-note {{in call to 'SS()'}}
+}
+}
+
+namespace UserDefinedConstructors {
+consteval int f(int x) {
+    return x;
+}
+extern int NonConst; // expected-note 2{{declared here}}
+
+struct ConstevalCtr {
+    int y;
+    int x = f(y);
+    consteval ConstevalCtr(int yy)
+    : y(f(yy)) {}
+};
+
+ConstevalCtr c1(1);
+ConstevalCtr c2(NonConst);
+// expected-error@-1 {{call to consteval function 'UserDefinedConstructors::ConstevalCtr::ConstevalCtr' is not a constant expression}} \
+// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}}
+
+struct ImmediateEscalating {
+    int y;
+    int x = f(y);
+    template<typename T>
+    constexpr ImmediateEscalating(T yy)
+    : y(f(yy)) {}
+};
+
+ImmediateEscalating c3(1);
+ImmediateEscalating c4(NonConst);
+// expected-error@-1 {{call to immediate function 'UserDefinedConstructors::ImmediateEscalating::ImmediateEscalating<int>' is not a constant expression}} \
+// expected-note@-1 {{read of non-const variable 'NonConst' is not allowed in a constant expression}}
+
+
+struct NonEscalating {
+    int y;
+    int x = f(this->y); // expected-error {{call to consteval function 'UserDefinedConstructors::f' is not a constant expression}} \
+                        // expected-note  {{declared here}} \
+                        // expected-note  {{use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}}
+    constexpr NonEscalating(int yy) : y(yy) {} // expected-note {{in the default initializer of 'x'}}
+};
+NonEscalating s = {1};
+
+}
+
+namespace AggregateInit {
+
+consteval int f(int x) {
+    return x;
+}
+
+struct S {
+    int i;
+    int j = f(i);
+};
+
+constexpr S  test(auto) {
+    return {};
+}
+
+S s = test(0);
+
+}
+
+namespace GlobalAggregateInit {
+
+consteval int f(int x) {
+    return x;
+}
+
+struct S {
+    int i;
+    int j = f(i); // expected-error {{call to consteval function 'GlobalAggregateInit::f' is not a constant expression}} \
+                  // expected-note {{in the default initializer of 'j'}} \
+                  // expected-note {{implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function}} \
+                  // expected-note {{declared here}}
+};
+
+S s(0);
+}
Index: clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
+++ clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
@@ -41,19 +41,21 @@
     }();
 };
 
-consteval int ub(int n) { // expected-note {{declared here}}
+consteval int ub(int n) {
     return 0/n;
 }
 
 struct InitWithLambda {
-    int b = [](int error = undefined()) { // expected-error {{cannot take address of consteval function 'undefined' outside of an immediate invocation}}
+    int b = [](int error = undefined()) {  // expected-note {{undefined function 'undefined' cannot be used in a constant expression}}
         return error;
     }();
-    int c = [](int error = sizeof(undefined()) + ub(0)) { // expected-error {{cannot take address of consteval function 'ub' outside of an immediate invocation}}
+    int c = [](int error = sizeof(undefined()) + ub(0)) {
 
         return error;
     }();
 } i;
+// expected-error@-1 {{call to immediate function 'InitWithLambda::InitWithLambda' is not a constant expression}} \
+// expected-note@-1 {{in call to 'InitWithLambda()'}}
 
 namespace ShouldNotCrash {
     template<typename T>
Index: clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/cxx2c-consteval-consteval.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -emit-llvm %s -std=c++2a -triple x86_64-unknown-linux-gnu -o %t.ll
+// RUN: FileCheck -input-file=%t.ll %s
+
+namespace GH63742 {
+
+void side_effect();
+consteval int f(int x) {
+    if (!__builtin_is_constant_evaluated()) side_effect();
+    return x;
+}
+struct SS {
+    int x = f(42);
+    SS();
+};
+SS::SS(){}
+
+}
+
+// CHECK-LABEL: @_ZN7GH637422SSC2Ev
+// CHECK-NOT:   call
+// CHECK:       store i32 42, ptr {{.*}}
+// CHECK:       ret void
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -6084,17 +6084,11 @@
   ImmediateCallVisitor(const ASTContext &Ctx) : Context(Ctx) {}
 
   bool HasImmediateCalls = false;
-  bool IsImmediateInvocation = false;
-
   bool shouldVisitImplicitCode() const { return true; }
 
   bool VisitCallExpr(CallExpr *E) {
-    if (const FunctionDecl *FD = E->getDirectCallee()) {
+    if (const FunctionDecl *FD = E->getDirectCallee())
       HasImmediateCalls |= FD->isImmediateFunction();
-      if (FD->isConsteval() && !E->isCXX11ConstantExpr(Context))
-        IsImmediateInvocation = true;
-    }
-
     return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
   }
 
@@ -6224,6 +6218,9 @@
   if (Field->isInvalidDecl())
     return ExprError();
 
+  CXXThisScopeRAII This(*this, Field->getParent(), Qualifiers(),
+                        Field->getParent() != nullptr);
+
   auto *ParentRD = cast<CXXRecordDecl>(Field->getParent());
 
   std::optional<ExpressionEvaluationContextRecord::InitializationContext>
@@ -6271,13 +6268,6 @@
   if (!NestedDefaultChecking)
     V.TraverseDecl(Field);
   if (V.HasImmediateCalls) {
-    // C++23 [expr.const]/p15
-    // An aggregate initialization is an immediate invocation
-    // if it evaluates a default member initializer that has a subexpression
-    // that is an immediate-escalating expression.
-    ExprEvalContexts.back().InImmediateFunctionContext |=
-        V.IsImmediateInvocation;
-
     ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
                                                                    CurContext};
     ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
@@ -18418,6 +18408,11 @@
     if (!CE.getInt())
       EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE);
   for (auto *DR : Rec.ReferenceToConsteval) {
+    // If the expression is immediate escalating, it is not an error;
+    // The outer context itself becomes immediate and further errors,
+    // if any, will be handled by DiagnoseImmediateEscalatingReason
+    if (DR->isImmediateEscalating())
+      continue;
     const auto *FD = cast<FunctionDecl>(DR->getDecl());
     const NamedDecl *ND = FD;
     if (const auto *MD = dyn_cast<CXXMethodDecl>(ND);
@@ -18443,8 +18438,15 @@
       SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address)
           << ND << isa<CXXRecordDecl>(ND) << FD->isConsteval();
       SemaRef.Diag(ND->getLocation(), diag::note_declared_at);
+      if (auto Context =
+              SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) {
+        SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer)
+            << Context->Decl;
+        SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at);
+      }
       if (FD->isImmediateEscalating() && !FD->isConsteval())
         SemaRef.DiagnoseImmediateEscalatingReason(FD);
+
     } else {
       SemaRef.MarkExpressionAsImmediateEscalating(DR);
     }
@@ -18893,6 +18895,12 @@
   // or of another default member initializer (ie a PotentiallyEvaluatedIfUsed
   // context), its initializers may not be referenced yet.
   if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Func)) {
+    EnterExpressionEvaluationContext EvalContext(
+        *this,
+        Constructor->isImmediateFunction()
+            ? ExpressionEvaluationContext::ImmediateFunctionContext
+            : ExpressionEvaluationContext::PotentiallyEvaluated,
+        Constructor);
     for (CXXCtorInitializer *Init : Constructor->inits()) {
       if (Init->isInClassMemberInitializer())
         runWithSufficientStackSpace(Init->getSourceLocation(), [&]() {
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -2438,13 +2438,12 @@
 }
 
 bool Sema::CheckImmediateEscalatingFunctionDefinition(
-    FunctionDecl *FD, bool HasImmediateEscalatingExpression) {
-  if (!FD->hasBody() || !getLangOpts().CPlusPlus20 ||
-      !FD->isImmediateEscalating())
+    FunctionDecl *FD, const sema::FunctionScopeInfo *FSI) {
+  if (!getLangOpts().CPlusPlus20 || !FD->isImmediateEscalating())
     return true;
   FD->setBodyContainsImmediateEscalatingExpressions(
-      HasImmediateEscalatingExpression);
-  if (HasImmediateEscalatingExpression) {
+      FSI->FoundImmediateEscalatingExpression);
+  if (FSI->FoundImmediateEscalatingExpression) {
     auto it = UndefinedButUsed.find(FD->getCanonicalDecl());
     if (it != UndefinedButUsed.end()) {
       Diag(it->second, diag::err_immediate_function_used_before_definition)
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -15575,8 +15575,7 @@
     if (FD) {
       FD->setBody(Body);
       FD->setWillHaveBody(false);
-      CheckImmediateEscalatingFunctionDefinition(
-          FD, FSI->FoundImmediateEscalatingExpression);
+      CheckImmediateEscalatingFunctionDefinition(FD, FSI);
 
       if (getLangOpts().CPlusPlus14) {
         if (!FD->isInvalidDecl() && Body && !FD->isDependentContext() &&
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -5319,6 +5319,10 @@
   const Decl *TargetDecl =
       OrigCallee.getAbstractInfo().getCalleeDecl().getDecl();
 
+  assert((!isa_and_present<FunctionDecl>(TargetDecl) ||
+          !cast<FunctionDecl>(TargetDecl)->isImmediateFunction()) &&
+         "trying to emit a call to an immediate function");
+
   CalleeType = getContext().getCanonicalType(CalleeType);
 
   auto PointeeType = cast<PointerType>(CalleeType)->getPointeeType();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1068,10 +1068,13 @@
   public:
     SynthesizedFunctionScope(Sema &S, DeclContext *DC)
         : S(S), SavedContext(S, DC) {
+      auto *FD = dyn_cast<FunctionDecl>(DC);
       S.PushFunctionScope();
       S.PushExpressionEvaluationContext(
-          Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
-      if (auto *FD = dyn_cast<FunctionDecl>(DC)) {
+          (FD && FD->isConsteval())
+              ? ExpressionEvaluationContext::ImmediateFunctionContext
+              : ExpressionEvaluationContext::PotentiallyEvaluated);
+      if (FD) {
         FD->setWillHaveBody(true);
         S.ExprEvalContexts.back().InImmediateFunctionContext =
             FD->isImmediateFunction();
@@ -1096,8 +1099,10 @@
     ~SynthesizedFunctionScope() {
       if (PushedCodeSynthesisContext)
         S.popCodeSynthesisContext();
-      if (auto *FD = dyn_cast<FunctionDecl>(S.CurContext))
+      if (auto *FD = dyn_cast<FunctionDecl>(S.CurContext)) {
         FD->setWillHaveBody(false);
+        S.CheckImmediateEscalatingFunctionDefinition(FD, S.getCurFunction());
+      }
       S.PopExpressionEvaluationContext();
       S.PopFunctionScopeInfo();
     }
@@ -6558,8 +6563,9 @@
   /// invocation.
   ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl);
 
-  bool CheckImmediateEscalatingFunctionDefinition(
-      FunctionDecl *FD, bool HasImmediateEscalatingExpression);
+  bool
+  CheckImmediateEscalatingFunctionDefinition(FunctionDecl *FD,
+                                             const sema::FunctionScopeInfo *);
 
   void MarkExpressionAsImmediateEscalating(Expr *E);
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to