diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 86304a54473ce..ae5e2dadac951 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -82,15 +82,27 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { if (DiscardResult) return this->discard(SubExpr); - if (SubExpr->getType()->isAnyComplexType()) - return this->delegate(SubExpr); + std::optional SubExprT = classify(SubExpr->getType()); + // Prepare storage for the result. + if (!Initializing && !SubExprT) { + std::optional LocalIndex = + allocateLocal(SubExpr, /*IsExtended=*/false); + if (!LocalIndex) + return false; + if (!this->emitGetPtrLocal(*LocalIndex, CE)) + return false; + } if (!this->visit(SubExpr)) return false; - if (std::optional SubExprT = classify(SubExpr->getType())) + if (SubExprT) return this->emitLoadPop(*SubExprT, CE); - return false; + + // If the subexpr type is not primitive, we need to perform a copy here. + // This happens for example in C when dereferencing a pointer of struct + // type. + return this->emitMemcpy(CE); } case CK_UncheckedDerivedToBase: @@ -3247,53 +3259,20 @@ bool ByteCodeExprGen::VisitDeclRefExpr(const DeclRefExpr *E) { // pointer to the actual value) instead of a pointer to the pointer to the // value. bool IsReference = D->getType()->isReferenceType(); - // Complex values are copied in the AST via a simply assignment or - // ltor cast. But we represent them as two-element arrays, which means - // we pass them around as pointers. So, to assignm from them, we will - // have to copy both (primitive) elements instead. - bool IsComplex = D->getType()->isAnyComplexType(); // Check for local/global variables and parameters. if (auto It = Locals.find(D); It != Locals.end()) { const unsigned Offset = It->second.Offset; - // FIXME: Fix the code duplication here with the code in the global case. - if (Initializing && IsComplex) { - PrimType ElemT = classifyComplexElementType(D->getType()); - for (unsigned I = 0; I != 2; ++I) { - if (!this->emitGetPtrLocal(Offset, E)) - return false; - if (!this->emitArrayElemPop(ElemT, I, E)) - return false; - if (!this->emitInitElem(ElemT, I, E)) - return false; - } - return true; - } - if (IsReference) return this->emitGetLocal(PT_Ptr, Offset, E); return this->emitGetPtrLocal(Offset, E); } else if (auto GlobalIndex = P.getGlobal(D)) { - if (Initializing && IsComplex) { - PrimType ElemT = classifyComplexElementType(D->getType()); - for (unsigned I = 0; I != 2; ++I) { - if (!this->emitGetPtrGlobal(*GlobalIndex, E)) - return false; - if (!this->emitArrayElemPop(ElemT, I, E)) - return false; - if (!this->emitInitElem(ElemT, I, E)) - return false; - } - return true; - } - if (IsReference) return this->emitGetGlobalPtr(*GlobalIndex, E); return this->emitGetPtrGlobal(*GlobalIndex, E); } else if (const auto *PVD = dyn_cast(D)) { if (auto It = this->Params.find(PVD); It != this->Params.end()) { - // FIXME: _Complex initializing case? if (IsReference || !It->second.IsPtr) return this->emitGetParamPtr(It->second.Offset, E); diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index ce7ed9cec3db3..dff8eed12428e 100644 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ b/clang/lib/AST/Interp/Descriptor.cpp @@ -233,9 +233,10 @@ static BlockMoveFn getMoveArrayPrim(PrimType Type) { Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst, bool IsTemporary, bool IsMutable) : Source(D), ElemSize(primSize(Type)), Size(ElemSize), - MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)), - DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) { + MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type), + IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), + CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), + MoveFn(getMovePrim(Type)) { assert(AllocSize >= Size); assert(Source && "Missing source"); } @@ -246,7 +247,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsMutable) : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), MDSize(MD.value_or(0)), - AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), + AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h index 0f64d678f3ef6..5160f07466fba 100644 --- a/clang/lib/AST/Interp/Descriptor.h +++ b/clang/lib/AST/Interp/Descriptor.h @@ -112,6 +112,10 @@ struct Descriptor final { const Record *const ElemRecord = nullptr; /// Descriptor of the array element. const Descriptor *const ElemDesc = nullptr; + /// The primitive type this descriptor was created for, + /// or the primitive element type in case this is + /// a primitive array. + const std::optional PrimT = std::nullopt; /// Flag indicating if the block is mutable. const bool IsConst = false; /// Flag indicating if a field is mutable. @@ -183,6 +187,11 @@ struct Descriptor final { return Size; } + PrimType getPrimType() const { + assert(isPrimitiveArray() || isPrimitive()); + return *PrimT; + } + /// Returns the allocated size, including metadata. unsigned getAllocSize() const { return AllocSize; } /// returns the size of an element when the structure is viewed as an array. diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index db80e2d59753f..507d91129d688 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -122,6 +122,9 @@ bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F, bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC, const Pointer &Ptr, const APSInt &IntValue); +/// Copy the contents of Src into Dest. +bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest); + /// Checks if the shift operation is legal. template bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS, @@ -1487,6 +1490,16 @@ bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) { return true; } +inline bool Memcpy(InterpState &S, CodePtr OpPC) { + const Pointer &Src = S.Stk.pop(); + Pointer &Dest = S.Stk.peek(); + + if (!CheckLoad(S, OpPC, Src)) + return false; + + return DoMemcpy(S, OpPC, Src, Dest); +} + //===----------------------------------------------------------------------===// // AddOffset, SubOffset //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index c500b9d502d70..8113254f8fa8e 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -1326,5 +1326,50 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC, return true; } +bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) { + assert(Src.isLive() && Dest.isLive()); + + const Descriptor *SrcDesc = Src.getFieldDesc(); + const Descriptor *DestDesc = Dest.getFieldDesc(); + + assert(!DestDesc->isPrimitive() && !SrcDesc->isPrimitive()); + + if (DestDesc->isPrimitiveArray()) { + assert(SrcDesc->isPrimitiveArray()); + assert(SrcDesc->getNumElems() == DestDesc->getNumElems()); + PrimType ET = DestDesc->getPrimType(); + for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) { + Pointer DestElem = Dest.atIndex(I); + TYPE_SWITCH(ET, { + DestElem.deref() = Src.atIndex(I).deref(); + DestElem.initialize(); + }); + } + return true; + } + + if (DestDesc->isRecord()) { + assert(SrcDesc->isRecord()); + assert(SrcDesc->ElemRecord == DestDesc->ElemRecord); + const Record *R = DestDesc->ElemRecord; + for (const Record::Field &F : R->fields()) { + Pointer DestField = Dest.atField(F.Offset); + if (std::optional FT = S.Ctx.classify(F.Decl->getType())) { + TYPE_SWITCH(*FT, { + DestField.deref() = Src.atField(F.Offset).deref(); + DestField.initialize(); + }); + } else { + return Invalid(S, OpPC); + } + } + return true; + } + + // FIXME: Composite types. + + return Invalid(S, OpPC); +} + } // namespace interp } // namespace clang diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 9b99aa0ccb558..0ed214af3548a 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -720,3 +720,5 @@ def CheckNonNullArg : Opcode { let Types = [PtrTypeClass]; let HasGroup = 1; } + +def Memcpy : Opcode; diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c index 8de6139efbea0..5caebd9ce6f14 100644 --- a/clang/test/AST/Interp/c.c +++ b/clang/test/AST/Interp/c.c @@ -201,3 +201,11 @@ void localCompoundLiteral(void) { // pedantic-ref-warning {{use of an empty initializer}} }; } + +/// struct copy +struct StrA {int a; }; +const struct StrA sa = { 12 }; +const struct StrA * const sb = &sa; +const struct StrA sc = *sb; +_Static_assert(sc.a == 12, ""); // pedantic-ref-warning {{GNU extension}} \ + // pedantic-expected-warning {{GNU extension}}