https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/224839
>From cd1b6f9af512f86e7a80e4a6230b3b56b503dde3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]> Date: Sat, 19 Sep 2026 15:47:00 +0200 Subject: [PATCH] default ctor --- clang/lib/AST/ByteCode/Compiler.cpp | 10 +++ clang/lib/AST/ByteCode/Interp.cpp | 63 +++++++++++++++++++ clang/lib/AST/ByteCode/Interp.h | 1 + clang/lib/AST/ByteCode/Opcodes.td | 5 ++ .../AST/ByteCode/Inputs/default-ctor.cppm | 12 ++++ .../test/AST/ByteCode/module-default-ctor.cpp | 11 ++++ 6 files changed, 102 insertions(+) create mode 100644 clang/test/AST/ByteCode/Inputs/default-ctor.cppm create mode 100644 clang/test/AST/ByteCode/module-default-ctor.cpp 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); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
