llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

<details>
<summary>Changes</summary>

Trivial default initializers from modules are not implicitly defined in the 
AST, so we would diagnose the attached test case as an undefined constructor. 
Do what the current interpreter does and special-case this.

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

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


6 Files Affected:

- (modified) clang/lib/AST/ByteCode/Compiler.cpp (+10) 
- (modified) clang/lib/AST/ByteCode/Interp.cpp (+63) 
- (modified) clang/lib/AST/ByteCode/Interp.h (+1) 
- (modified) clang/lib/AST/ByteCode/Opcodes.td (+5) 
- (added) clang/test/AST/ByteCode/Inputs/default-ctor.cppm (+12) 
- (added) clang/test/AST/ByteCode/module-default-ctor.cpp (+11) 


``````````diff
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp 
b/clang/lib/AST/ByteCode/Compiler.cpp
index d7bdb2f217a9a..83b118c18b7aa 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3899,6 +3899,16 @@ bool Compiler<Emitter>::VisitCXXConstructExpr(const 
CXXConstructExpr *E) {
         return true;
     }
 
+    // Trivial default constructors might never be implicitly defined by the
+    // AST, so we need to special-case them here.
+    if (Ctor->isTrivial() && Ctor->isDefaultConstructor()) {
+      if (!this->emitDefaultInit(Ctor, E))
+        return false;
+      if (DiscardResult)
+        return this->emitPopPtr(E);
+      return true;
+    }
+
     // Avoid materializing a temporary for an elidable copy/move constructor.
     if (!ZeroInit && E->isElidable()) {
       const Expr *SrcObj = E->getArg(0);
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index ab6b474503a3b..dde7e54d86977 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -2683,6 +2683,69 @@ bool MarkDestroyed(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
+// Initializes all bases and virtual bases.
+// Only starts the lifetime of fields, but doesn't initialize them.
+static void initBasesRecurse(PtrView Ptr) {
+  assert(Ptr.getRecord());
+
+  const Record *R = Ptr.getRecord();
+  for (const Record::Base &B : R->bases()) {
+    PtrView BasePtr = Ptr.atField(B.Offset);
+    BasePtr.initialize();
+    BasePtr.startLifetime();
+    initBasesRecurse(BasePtr);
+  }
+
+  for (const Record::Field &F : R->fields()) {
+    PtrView FieldPtr = Ptr.atField(F.Offset);
+    FieldPtr.startLifetime();
+    if (FieldPtr.getRecord())
+      initBasesRecurse(FieldPtr);
+  }
+
+  for (const Record::Base &B : R->virtual_bases()) {
+    PtrView BasePtr = Ptr.atField(B.Offset);
+    BasePtr.initialize();
+    BasePtr.startLifetime();
+    initBasesRecurse(BasePtr);
+  }
+}
+
+bool DefaultInit(InterpState &S, CodePtr OpPC, const CXXConstructorDecl *Ctor) 
{
+  auto Ptr = S.Stk.peek<Pointer>();
+
+  if (!Ptr.isBlockPointer())
+    return false;
+  const Record *R = Ptr.getRecord();
+  if (!R)
+    return false;
+
+  if (Ctor->isInvalidDecl() || Ctor->getParent()->isInvalidDecl())
+    return false;
+
+  if (!Ctor->isConstexpr()) {
+    if (S.getLangOpts().CPlusPlus11) {
+      // FIXME: If DiagDecl is an implicitly-declared special member function,
+      // we should be much more explicit about why it's not constexpr.
+      S.CCEDiag(S.Current->getSource(OpPC),
+                diag::note_constexpr_invalid_function, 1)
+          << /*IsConstexpr*/ 0 << /*IsConstructor*/ 1 << Ctor;
+      S.Note(Ctor->getLocation(), diag::note_declared_at);
+    } else {
+      S.CCEDiag(S.Current->getSource(OpPC),
+                diag::note_invalid_subexpr_in_const_expr);
+    }
+  }
+
+  Ptr.startLifetime();
+  Ptr.initialize();
+
+  startLifetimeRecurse(Ptr.view());
+  initBasesRecurse(Ptr.view());
+
+  return true;
+}
+
 bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
                           std::optional<uint64_t> ArraySize) {
   Pointer &Orig = S.Stk.peek<Pointer>();
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index edbf2adcda637..f7d396b720767 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1642,6 +1642,7 @@ bool PseudoDtor(InterpState &S, CodePtr OpPC);
 bool StartThisLifetime(InterpState &S);
 bool StartThisLifetime1(InterpState &S);
 bool MarkDestroyed(InterpState &S, CodePtr OpPC);
+bool DefaultInit(InterpState &S, CodePtr OpPC, const CXXConstructorDecl *Ctor);
 
 /// 1) Pops the value from the stack.
 /// 2) Writes the value to the local variable with the
diff --git a/clang/lib/AST/ByteCode/Opcodes.td 
b/clang/lib/AST/ByteCode/Opcodes.td
index 11ba6ddf41b74..afed0014c1a10 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -73,6 +73,7 @@ def ArgPrimType : ArgType { let Name = "PrimType"; }
 def ArgEnumDecl : ArgType { let Name = "const EnumDecl *"; }
 def ArgTypePtr : ArgType { let Name = "const Type *"; }
 def ArgDeclOrExpr : ArgType { let Name = "DeclOrExpr"; }
+def ArgCtorDecl : ArgType { let Name = "const CXXConstructorDecl *"; }
 
 
//===----------------------------------------------------------------------===//
 // Classes of types instructions operate on.
@@ -479,6 +480,10 @@ def StartThisLifetime1 : Opcode {
   let NeedsOpPC = 0;
 }
 
+def DefaultInit : Opcode {
+  let Args = [ArgCtorDecl];
+}
+
 def CheckDecl : Opcode {
   let Args = [ArgVarDecl];
   let NeedsOpPC = 0;
diff --git a/clang/test/AST/ByteCode/Inputs/default-ctor.cppm 
b/clang/test/AST/ByteCode/Inputs/default-ctor.cppm
new file mode 100644
index 0000000000000..ee945435eaea1
--- /dev/null
+++ b/clang/test/AST/ByteCode/Inputs/default-ctor.cppm
@@ -0,0 +1,12 @@
+export module m;
+
+export struct allocator_like {
+  constexpr allocator_like() noexcept = default;
+};
+
+export struct box {
+  allocator_like allocation;
+  int value = 42;
+  constexpr box() = default;
+  [[nodiscard]] constexpr int get() const { return value; }
+};
diff --git a/clang/test/AST/ByteCode/module-default-ctor.cpp 
b/clang/test/AST/ByteCode/module-default-ctor.cpp
new file mode 100644
index 0000000000000..4883b2a6e28ba
--- /dev/null
+++ b/clang/test/AST/ByteCode/module-default-ctor.cpp
@@ -0,0 +1,11 @@
+// RUN: mkdir -p %t
+// RUN: %clang    -std=c++20 %p/Inputs/default-ctor.cppm --precompile -o 
%t/default-ctor.pcm
+// RUN: %clang -c -std=c++20 -fmodule-file=m=%t/default-ctor.pcm 
-fexperimental-new-constant-interpreter %s
+import m;
+
+
+consteval int evaluate() {
+  box b;
+  return b.get();
+}
+static_assert(evaluate() == 42);

``````````

</details>


https://github.com/llvm/llvm-project/pull/224839
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to