https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/219952
>From aa8d3df0543f5964eabd01cfd5ec0c49499b9f49 Mon Sep 17 00:00:00 2001 From: yronglin <[email protected]> Date: Mon, 31 Aug 2026 06:47:04 -0700 Subject: [PATCH] [clang][bytecode] Give a discarded composite prvalue a result object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Timm Bäder <[email protected]> Signed-off-by: yronglin <[email protected]> --- clang/lib/AST/ByteCode/Compiler.cpp | 40 ++++++++++++++++++++--------- clang/test/AST/ByteCode/records.cpp | 29 +++++++++++++++++++-- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 55abd51659b46..e566d8f64db9e 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -265,7 +265,9 @@ template <class Emitter> class InitStackScope final { public: InitStackScope(Compiler<Emitter> *Ctx, bool Active) : Ctx(Ctx), OldValue(Ctx->InitStackActive), Active(Active) { - Ctx->InitStackActive = Active; + // An explicit initializer nested in a default member initializer still + // needs the surrounding default initializer's `this` reconstruction. + Ctx->InitStackActive = OldValue || Active; if (Active) Ctx->InitStack.push_back(InitLink::DIE()); } @@ -2308,24 +2310,38 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, return this->emitInvalid(E); } - // Handle discarding first. - if (DiscardResult) { - for (const Expr *Init : Inits) { - if (!this->discard(Init)) - return false; - } - return true; - } - - // Primitive values. + // Primitive values. A discarded one can simply discard each initializer; + // there is no object to establish. if (OptPrimType T = classify(QT)) { - assert(!DiscardResult); + if (DiscardResult) { + for (const Expr *Init : Inits) { + if (!this->discard(Init)) + return false; + } + return true; + } if (Inits.size() == 0) return this->visitZeroInitializer(*T, QT, E); assert(Inits.size() == 1); return this->delegate(Inits[0]); } + assert(!canClassify(E->getType())); + + // A composite prvalue needs somewhere to live even when it is discarded: a + // default member initializer may read subobjects initialized earlier in this + // same list, so those have to actually be written and `this` has to denote + // the object. Materialize one and initialize into it. + if (DiscardResult && !Initializing) { + UnsignedOrNone LocalIndex = allocateLocal(E); + if (!LocalIndex) + return false; + if (!this->emitGetPtrLocal(*LocalIndex, E)) + return false; + InitLinkScope<Emitter> ILS2(this, InitLink::Temp(*LocalIndex)); + return this->visitInitializerPop(E); + } + if (QT->isRecordType()) { const Record *R = getRecord(QT); diff --git a/clang/test/AST/ByteCode/records.cpp b/clang/test/AST/ByteCode/records.cpp index 36b5cb62fe95f..e522072802308 100644 --- a/clang/test/AST/ByteCode/records.cpp +++ b/clang/test/AST/ByteCode/records.cpp @@ -1302,11 +1302,36 @@ namespace { }; constexpr int a() { int x = 1; - int f = B{x}.x; + { + B b{x}; + } + return x; + } + static_assert(a() == 0); + + constexpr int discarded() { + int x = 1; B{x}; // both-warning {{expression result unused}} + return x; + } - return 1; + /// Before the result object was allocated, this could not be evaluated at + /// all. The temporary 'B' is not destroyed until the end of the enclosing + /// full-expression, so 'x' still reads 1 here, matching legacy evaluator. + /// + /// FIXME: See https://github.com/llvm/llvm-project/issues/85601. + static_assert(discarded() == 1); + + /// A const-qualified composite result is writable while under construction. + constexpr int decrement(int &x) { + return --x; } + struct DMIConstComposite { + int a; + int b = decrement(a); + }; + constexpr DMIConstComposite c{1}; + static_assert(c.a == 0); } #endif _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
