Author: Timm Bäder Date: 2024-05-23T09:17:56+02:00 New Revision: af6812085cc7a7251a3095acbc96343ce660f135
URL: https://github.com/llvm/llvm-project/commit/af6812085cc7a7251a3095acbc96343ce660f135 DIFF: https://github.com/llvm/llvm-project/commit/af6812085cc7a7251a3095acbc96343ce660f135.diff LOG: [clang][Interp] Fix initializing a union from an InitLIstExpr We can't just count the field to initialize but need to consult the InitListExpr to give us the right field. Added: Modified: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Interp.h clang/test/AST/Interp/unions.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 50c85bf7d35f5..e64d3a94b5091 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1050,38 +1050,71 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, if (T->isRecordType()) { const Record *R = getRecord(E->getType()); - if (Inits.size() == 1 && E->getType() == Inits[0]->getType()) { + if (Inits.size() == 1 && E->getType() == Inits[0]->getType()) return this->visitInitializer(Inits[0]); + + auto initPrimitiveField = [=](const Record::Field *FieldToInit, + const Expr *Init, PrimType T) -> bool { + if (!this->visit(Init)) + return false; + + if (FieldToInit->isBitField()) { + if (!this->emitInitBitField(T, FieldToInit, E)) + return false; + } else { + if (!this->emitInitField(T, FieldToInit->Offset, E)) + return false; + } + return this->emitPopPtr(E); + }; + + auto initCompositeField = [=](const Record::Field *FieldToInit, + const Expr *Init) -> bool { + // Non-primitive case. Get a pointer to the field-to-initialize + // on the stack and recurse into visitInitializer(). + if (!this->emitGetPtrField(FieldToInit->Offset, Init)) + return false; + if (!this->visitInitializer(Init)) + return false; + return this->emitPopPtr(E); + }; + + if (R->isUnion()) { + assert(Inits.size() == 1); + const Expr *Init = Inits[0]; + const FieldDecl *FToInit = nullptr; + if (const auto *ILE = dyn_cast<InitListExpr>(E)) + FToInit = ILE->getInitializedFieldInUnion(); + else + FToInit = cast<CXXParenListInitExpr>(E)->getInitializedFieldInUnion(); + + if (!this->emitDupPtr(E)) + return false; + + const Record::Field *FieldToInit = R->getField(FToInit); + if (std::optional<PrimType> T = classify(Init)) { + if (!initPrimitiveField(FieldToInit, Init, *T)) + return false; + } else { + if (!initCompositeField(FieldToInit, Init)) + return false; + } + return this->emitFinishInit(E); } + assert(!R->isUnion()); unsigned InitIndex = 0; for (const Expr *Init : Inits) { // Skip unnamed bitfields. while (InitIndex < R->getNumFields() && R->getField(InitIndex)->Decl->isUnnamedBitField()) ++InitIndex; - - // Potentially skip ahead. This is especially relevant in unions. - if (const auto *D = dyn_cast<CXXDefaultInitExpr>(Init)) - InitIndex = D->getField()->getFieldIndex(); - if (!this->emitDupPtr(E)) return false; if (std::optional<PrimType> T = classify(Init)) { const Record::Field *FieldToInit = R->getField(InitIndex); - if (!this->visit(Init)) - return false; - - if (FieldToInit->isBitField()) { - if (!this->emitInitBitField(*T, FieldToInit, E)) - return false; - } else { - if (!this->emitInitField(*T, FieldToInit->Offset, E)) - return false; - } - - if (!this->emitPopPtr(E)) + if (!initPrimitiveField(FieldToInit, Init, *T)) return false; ++InitIndex; } else { @@ -1099,21 +1132,13 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, // into the Record's fields. } else { const Record::Field *FieldToInit = R->getField(InitIndex); - // Non-primitive case. Get a pointer to the field-to-initialize - // on the stack and recurse into visitInitializer(). - if (!this->emitGetPtrField(FieldToInit->Offset, Init)) - return false; - - if (!this->visitInitializer(Init)) - return false; - - if (!this->emitPopPtr(E)) + if (!initCompositeField(FieldToInit, Init)) return false; ++InitIndex; } } } - return true; + return this->emitFinishInit(E); } if (T->isArrayType()) { @@ -1137,7 +1162,7 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, } } - return true; + return this->emitFinishInit(E); } if (const auto *ComplexTy = E->getType()->getAs<ComplexType>()) { diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 7ef963c810c82..8430a7de24dff 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1337,14 +1337,15 @@ inline bool FinishInitPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (Ptr.canBeInitialized()) Ptr.initialize(); + Ptr.activate(); return true; } inline bool FinishInit(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (Ptr.canBeInitialized()) Ptr.initialize(); + Ptr.activate(); return true; } diff --git a/clang/test/AST/Interp/unions.cpp b/clang/test/AST/Interp/unions.cpp index 46ba514b47b98..bc5604c2b2d04 100644 --- a/clang/test/AST/Interp/unions.cpp +++ b/clang/test/AST/Interp/unions.cpp @@ -18,3 +18,15 @@ constexpr U1 u1{}; static_assert(u1.f == 3.0, ""); static_assert(u1.i == 1, ""); // both-error {{not an integral constant expression}} \ // both-note {{read of member 'i' of union with active member 'f'}} + + + +union A { + int a; + double d; +}; +constexpr A aa = {1, 2.0}; // both-error {{excess elements in union initializer}} +constexpr A ab = {.d = 1.0}; +static_assert(ab.d == 1.0, ""); +static_assert(ab.a == 1, ""); // both-error {{not an integral constant expression}} \ + // both-note {{read of member 'a' of union with active member 'd'}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits