Author: Timm Baeder Date: 2026-09-20T07:35:08+02:00 New Revision: 780937f68bfd726b3e45dd2de14d0d152e075147
URL: https://github.com/llvm/llvm-project/commit/780937f68bfd726b3e45dd2de14d0d152e075147 DIFF: https://github.com/llvm/llvm-project/commit/780937f68bfd726b3e45dd2de14d0d152e075147.diff LOG: [clang][bytecode] Add a special opcode for trivial default initializers (#224839) 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 Added: clang/test/AST/ByteCode/Inputs/default-ctor.cppm clang/test/AST/ByteCode/module-default-ctor.cpp Modified: clang/lib/AST/ByteCode/Compiler.cpp clang/lib/AST/ByteCode/Interp.cpp clang/lib/AST/ByteCode/Interp.h clang/lib/AST/ByteCode/Opcodes.td Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index a2a3516ac3d78..2d5dbdfda003c 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -3903,6 +3903,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); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
