[Clang] Fix consteval propagation for aggregates and defaulted constructors

This patch does a few things:

* Fix aggregate initialization.
  When an aggregate has an initializer that is immediate-escalating,
  the context in which it is used automatically becomes an immediate function.
  The wording does that by rpretending an aggregate initialization is itself
  an invocation which is not really how clang works, so my previous attempt
  was... wrong.

* Fix initialization of defaulted constructors with immediate escalating
  default member initializers.
  The wording was silent about that case and I did not handled it fully
  https://cplusplus.github.io/CWG/issues/2760.html

* Fix diagnostics
  In some cases clang would produce additional and unhelpful
  diagnostics by listing the invalid references to consteval
  function that appear in immediate escalating functions

Fixes https://github.com/llvm/llvm-project/issues/63742

Reviewed By: aaron.ballman, #clang-language-wg, Fznamznon

Differential Revision: https://reviews.llvm.org/D155175
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 95b04fe..604875c 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2727,7 +2727,11 @@
     TRY_TO(TraverseStmt(S->getExpr()));
 })
 
-DEF_TRAVERSE_STMT(CXXDefaultInitExpr, {})
+DEF_TRAVERSE_STMT(CXXDefaultInitExpr, {
+  if (getDerived().shouldVisitImplicitCode())
+    TRY_TO(TraverseStmt(S->getExpr()));
+})
+
 DEF_TRAVERSE_STMT(CXXDeleteExpr, {})
 DEF_TRAVERSE_STMT(ExprWithCleanups, {})
 DEF_TRAVERSE_STMT(CXXInheritedCtorInitExpr, {})
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b87414d..fe4c300 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2685,7 +2685,8 @@
   "immediate function %0 used before it is defined">;
 
 def note_immediate_function_reason : Note<
-  "%0 is an immediate function because its body "
+  "%0 is an immediate %select{function|constructor}5 because "
+  "%select{its body|the%select{| default}7 initializer of %8}6 "
   "%select{evaluates the address of %select{an immediate|a consteval}2 "
   "function %1|contains a call to %select{an immediate|a consteval}2 "
   "%select{function|constructor}4 %1 and that call is not a constant "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 082fa25..3418a37 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1078,10 +1078,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();
@@ -1106,8 +1109,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();
     }
@@ -6578,11 +6583,11 @@
   ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl);
 
   bool CheckImmediateEscalatingFunctionDefinition(
-      FunctionDecl *FD, bool HasImmediateEscalatingExpression);
+      FunctionDecl *FD, const sema::FunctionScopeInfo *FSI);
 
   void MarkExpressionAsImmediateEscalating(Expr *E);
 
-  void DiagnoseImmediateEscalatingReason(const clang::FunctionDecl *FD);
+  void DiagnoseImmediateEscalatingReason(FunctionDecl *FD);
 
   bool CompleteConstructorCall(CXXConstructorDecl *Constructor,
                                QualType DeclInitType, MultiExprArg ArgsPtr,
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index a585abf..ed6095f 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/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();
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e2beb77..07a9dec 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -4438,13 +4438,10 @@
 /// GetAddrOfFunction - Return the address of the given function.  If Ty is
 /// non-null, then this function will use the specified type if it has to
 /// create it (this occurs when we see a definition of the function).
-llvm::Constant *CodeGenModule::GetAddrOfFunction(GlobalDecl GD,
-                                                 llvm::Type *Ty,
-                                                 bool ForVTable,
-                                                 bool DontDefer,
-                                              ForDefinition_t IsForDefinition) {
-  assert(!cast<FunctionDecl>(GD.getDecl())->isImmediateFunction() &&
-         "an immediate function should never be emitted");
+llvm::Constant *
+CodeGenModule::GetAddrOfFunction(GlobalDecl GD, llvm::Type *Ty, bool ForVTable,
+                                 bool DontDefer,
+                                 ForDefinition_t IsForDefinition) {
   // If there was no specific requested type, just convert it now.
   if (!Ty) {
     const auto *FD = cast<FunctionDecl>(GD.getDecl());
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 8db77f8..a4bf579 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/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() &&
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 40fbaa0..c59ab8b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -46,6 +46,7 @@
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include <map>
 #include <optional>
 #include <set>
@@ -2438,13 +2439,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)
@@ -2458,65 +2458,92 @@
   return true;
 }
 
-void Sema::DiagnoseImmediateEscalatingReason(const FunctionDecl *FD) {
+void Sema::DiagnoseImmediateEscalatingReason(FunctionDecl *FD) {
   assert(FD->isImmediateEscalating() && !FD->isConsteval() &&
          "expected an immediate function");
   assert(FD->hasBody() && "expected the function to have a body");
   struct ImmediateEscalatingExpressionsVisitor
       : public RecursiveASTVisitor<ImmediateEscalatingExpressionsVisitor> {
+
     using Base = RecursiveASTVisitor<ImmediateEscalatingExpressionsVisitor>;
     Sema &SemaRef;
-    const FunctionDecl *FD;
-    ImmediateEscalatingExpressionsVisitor(Sema &SemaRef, const FunctionDecl *FD)
-        : SemaRef(SemaRef), FD(FD) {}
+
+    const FunctionDecl *ImmediateFn;
+    bool ImmediateFnIsConstructor;
+    CXXConstructorDecl *CurrentConstructor = nullptr;
+    CXXCtorInitializer *CurrentInit = nullptr;
+
+    ImmediateEscalatingExpressionsVisitor(Sema &SemaRef, FunctionDecl *FD)
+        : SemaRef(SemaRef), ImmediateFn(FD),
+          ImmediateFnIsConstructor(isa<CXXConstructorDecl>(FD)) {}
 
     bool shouldVisitImplicitCode() const { return true; }
     bool shouldVisitLambdaBody() const { return false; }
 
+    void Diag(const Expr *E, const FunctionDecl *Fn, bool IsCall) {
+      SourceLocation Loc = E->getBeginLoc();
+      SourceRange Range = E->getSourceRange();
+      if (CurrentConstructor && CurrentInit) {
+        Loc = CurrentConstructor->getLocation();
+        Range = CurrentInit->isWritten() ? CurrentInit->getSourceRange()
+                                         : SourceRange();
+      }
+      SemaRef.Diag(Loc, diag::note_immediate_function_reason)
+          << ImmediateFn << Fn << Fn->isConsteval() << IsCall
+          << isa<CXXConstructorDecl>(Fn) << ImmediateFnIsConstructor
+          << (CurrentInit != nullptr)
+          << (CurrentInit && !CurrentInit->isWritten())
+          << (CurrentInit ? CurrentInit->getAnyMember() : nullptr) << Range;
+    }
     bool TraverseCallExpr(CallExpr *E) {
       if (const auto *DR =
               dyn_cast<DeclRefExpr>(E->getCallee()->IgnoreImplicit());
           DR && DR->isImmediateEscalating()) {
-        SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason)
-            << FD << E->getDirectCallee() << E->getDirectCallee()->isConsteval()
-            << /*Call*/ 1 << /*Function*/ 0 << E->getSourceRange();
+        Diag(E, E->getDirectCallee(), /*IsCall=*/true);
+        return false;
       }
-      for (auto A : E->arguments()) {
-        getDerived().TraverseStmt(A);
-      }
+
+      for (Expr *A : E->arguments())
+        if (!getDerived().TraverseStmt(A))
+          return false;
+
       return true;
     }
+
     bool VisitDeclRefExpr(DeclRefExpr *E) {
       if (const auto *ReferencedFn = dyn_cast<FunctionDecl>(E->getDecl());
           ReferencedFn && E->isImmediateEscalating()) {
-        SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason)
-            << FD << ReferencedFn << ReferencedFn->isConsteval()
-            << /*Address*/ 0 << /*Function*/ 0 << E->getSourceRange();
+        Diag(E, ReferencedFn, /*IsCall=*/false);
+        return false;
       }
+
       return true;
     }
+
     bool VisitCXXConstructExpr(CXXConstructExpr *E) {
       CXXConstructorDecl *D = E->getConstructor();
       if (E->isImmediateEscalating()) {
-        SemaRef.Diag(E->getBeginLoc(), diag::note_immediate_function_reason)
-            << FD << D << D->isConsteval() << /*Call*/ 1 << /*Constructor*/ 1
-            << E->getSourceRange();
+        Diag(E, D, /*IsCall=*/true);
+        return false;
       }
       return true;
     }
 
-    // These nodes can never contain an immediate escalating expression,
-    // we can skip them to avoid unecessary work.
-    bool TraverseDecl(Decl *D) {
-      if (isa<FunctionDecl, RecordDecl>(D))
-        return true;
-      return Base::TraverseDecl(D);
+    bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
+      llvm::SaveAndRestore RAII(CurrentInit, Init);
+      return Base::TraverseConstructorInitializer(Init);
     }
+
+    bool TraverseCXXConstructorDecl(CXXConstructorDecl *Ctr) {
+      llvm::SaveAndRestore RAII(CurrentConstructor, Ctr);
+      return Base::TraverseCXXConstructorDecl(Ctr);
+    }
+
     bool TraverseType(QualType T) { return true; }
     bool VisitBlockExpr(BlockExpr *T) { return true; }
 
   } Visitor(*this, FD);
-  Visitor.TraverseStmt(FD->getBody());
+  Visitor.TraverseDecl(FD);
 }
 
 /// Get the class that is directly named by the current context. This is the
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 7163834..2716b66 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/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,8 @@
   if (Field->isInvalidDecl())
     return ExprError();
 
+  CXXThisScopeRAII This(*this, Field->getParent(), Qualifiers());
+
   auto *ParentRD = cast<CXXRecordDecl>(Field->getParent());
 
   std::optional<ExpressionEvaluationContextRecord::InitializationContext>
@@ -6271,13 +6267,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 =
@@ -18445,7 +18434,12 @@
     if (!CE.getInt())
       EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE);
   for (auto *DR : Rec.ReferenceToConsteval) {
-    const auto *FD = cast<FunctionDecl>(DR->getDecl());
+    // 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;
+    auto *FD = cast<FunctionDecl>(DR->getDecl());
     const NamedDecl *ND = FD;
     if (const auto *MD = dyn_cast<CXXMethodDecl>(ND);
         MD && (MD->isLambdaStaticInvoker() || isLambdaCallOperator(MD)))
@@ -18470,8 +18464,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);
     }
@@ -18920,6 +18921,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(), [&]() {
diff --git a/clang/test/CodeGenCXX/cxx2c-consteval-propagate.cpp b/clang/test/CodeGenCXX/cxx2c-consteval-propagate.cpp
new file mode 100644
index 0000000..df5191b
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-consteval-propagate.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -emit-llvm %s -std=c++20 -triple x86_64-unknown-linux-gnu -o - | FileCheck %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
diff --git a/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp b/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
index da8315a..be8f7cc 100644
--- a/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
+++ b/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}}
+struct InitWithLambda { // expected-note {{'InitWithLambda' is an immediate constructor because the default initializer of 'b' contains a call to a consteval function 'undefined' and that call is not a constant expression}}
+    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>
diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
index 5e149dd..333b98d 100644
--- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
+++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++2a -emit-llvm-only -Wno-unused-value %s -verify
-// RUN: %clang_cc1 -std=c++2b -emit-llvm-only -Wno-unused-value %s -verify
+// RUN: %clang_cc1 -std=c++2a -Wno-unused-value %s -verify
+// RUN: %clang_cc1 -std=c++2b -Wno-unused-value %s -verify
 
 consteval int id(int i) { return i; }
 constexpr char id(char c) { return c; }
@@ -34,7 +34,7 @@
 
 template <typename T>
 constexpr T h(T t = id(x)) { // expected-note {{read of non-const variable 'x' is not allowed in a constant expression}} \
-                             // expected-note 2{{'hh<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
+                             // expected-note {{'hh<int>' is an immediate function because its body contains a call to a consteval function 'id' and that call is not a constant expression}}
     return t;
 }
 
@@ -164,3 +164,169 @@
 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}} \
+                 // expected-note {{'immediate<int>' is an immediate function because its body contains a call to a consteval function 'f' and that call is not 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 y = f(1); // Ok
+  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 = {};
+constinit S2 s2 = {};
+
+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 a = sizeof(f(0)); // Ok
+  int x = f(0); // expected-note {{undefined function 'f' cannot be used in a constant expression}}
+
+  SS() = default; // expected-note {{'SS' is an immediate constructor because the default initializer of 'x' contains a call to a consteval function 'f' and that call is not a constant expression}}
+};
+
+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) // expected-note {{ImmediateEscalating<int>' is an immediate constructor because the initializer of 'y' contains a call to a consteval function 'f' and that call is not a constant expression}}
+    : 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 {{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); // expected-note {{in the default initializer of 'j'}}
+
+}