Skip to content

Commit b97c129

Browse files
committed
[clang][Interp] Fix non-primitive ltor casts
This doesn't happen in C++ since it will instead call the struct's copy constructor. However, in C, this needs to work.
1 parent 516ccce commit b97c129

File tree

7 files changed

+98
-41
lines changed

7 files changed

+98
-41
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,27 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
8282
if (DiscardResult)
8383
return this->discard(SubExpr);
8484

85-
if (SubExpr->getType()->isAnyComplexType())
86-
return this->delegate(SubExpr);
85+
std::optional<PrimType> SubExprT = classify(SubExpr->getType());
86+
// Prepare storage for the result.
87+
if (!Initializing && !SubExprT) {
88+
std::optional<unsigned> LocalIndex =
89+
allocateLocal(SubExpr, /*IsExtended=*/false);
90+
if (!LocalIndex)
91+
return false;
92+
if (!this->emitGetPtrLocal(*LocalIndex, CE))
93+
return false;
94+
}
8795

8896
if (!this->visit(SubExpr))
8997
return false;
9098

91-
if (std::optional<PrimType> SubExprT = classify(SubExpr->getType()))
99+
if (SubExprT)
92100
return this->emitLoadPop(*SubExprT, CE);
93-
return false;
101+
102+
// If the subexpr type is not primitive, we need to perform a copy here.
103+
// This happens for example in C when dereferencing a pointer of struct
104+
// type.
105+
return this->emitMemcpy(CE);
94106
}
95107

96108
case CK_UncheckedDerivedToBase:
@@ -3247,53 +3259,20 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
32473259
// pointer to the actual value) instead of a pointer to the pointer to the
32483260
// value.
32493261
bool IsReference = D->getType()->isReferenceType();
3250-
// Complex values are copied in the AST via a simply assignment or
3251-
// ltor cast. But we represent them as two-element arrays, which means
3252-
// we pass them around as pointers. So, to assignm from them, we will
3253-
// have to copy both (primitive) elements instead.
3254-
bool IsComplex = D->getType()->isAnyComplexType();
32553262

32563263
// Check for local/global variables and parameters.
32573264
if (auto It = Locals.find(D); It != Locals.end()) {
32583265
const unsigned Offset = It->second.Offset;
3259-
// FIXME: Fix the code duplication here with the code in the global case.
3260-
if (Initializing && IsComplex) {
3261-
PrimType ElemT = classifyComplexElementType(D->getType());
3262-
for (unsigned I = 0; I != 2; ++I) {
3263-
if (!this->emitGetPtrLocal(Offset, E))
3264-
return false;
3265-
if (!this->emitArrayElemPop(ElemT, I, E))
3266-
return false;
3267-
if (!this->emitInitElem(ElemT, I, E))
3268-
return false;
3269-
}
3270-
return true;
3271-
}
3272-
32733266
if (IsReference)
32743267
return this->emitGetLocal(PT_Ptr, Offset, E);
32753268
return this->emitGetPtrLocal(Offset, E);
32763269
} else if (auto GlobalIndex = P.getGlobal(D)) {
3277-
if (Initializing && IsComplex) {
3278-
PrimType ElemT = classifyComplexElementType(D->getType());
3279-
for (unsigned I = 0; I != 2; ++I) {
3280-
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
3281-
return false;
3282-
if (!this->emitArrayElemPop(ElemT, I, E))
3283-
return false;
3284-
if (!this->emitInitElem(ElemT, I, E))
3285-
return false;
3286-
}
3287-
return true;
3288-
}
3289-
32903270
if (IsReference)
32913271
return this->emitGetGlobalPtr(*GlobalIndex, E);
32923272

32933273
return this->emitGetPtrGlobal(*GlobalIndex, E);
32943274
} else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
32953275
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
3296-
// FIXME: _Complex initializing case?
32973276
if (IsReference || !It->second.IsPtr)
32983277
return this->emitGetParamPtr(It->second.Offset, E);
32993278

clang/lib/AST/Interp/Descriptor.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,10 @@ static BlockMoveFn getMoveArrayPrim(PrimType Type) {
233233
Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
234234
bool IsConst, bool IsTemporary, bool IsMutable)
235235
: Source(D), ElemSize(primSize(Type)), Size(ElemSize),
236-
MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst),
237-
IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)),
238-
DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) {
236+
MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), PrimT(Type),
237+
IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
238+
CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
239+
MoveFn(getMovePrim(Type)) {
239240
assert(AllocSize >= Size);
240241
assert(Source && "Missing source");
241242
}
@@ -246,7 +247,7 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
246247
bool IsMutable)
247248
: Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
248249
MDSize(MD.value_or(0)),
249-
AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)),
250+
AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)), PrimT(Type),
250251
IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
251252
IsArray(true), CtorFn(getCtorArrayPrim(Type)),
252253
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {

clang/lib/AST/Interp/Descriptor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ struct Descriptor final {
112112
const Record *const ElemRecord = nullptr;
113113
/// Descriptor of the array element.
114114
const Descriptor *const ElemDesc = nullptr;
115+
/// The primitive type this descriptor was created for,
116+
/// or the primitive element type in case this is
117+
/// a primitive array.
118+
const std::optional<PrimType> PrimT = std::nullopt;
115119
/// Flag indicating if the block is mutable.
116120
const bool IsConst = false;
117121
/// Flag indicating if a field is mutable.
@@ -183,6 +187,11 @@ struct Descriptor final {
183187
return Size;
184188
}
185189

190+
PrimType getPrimType() const {
191+
assert(isPrimitiveArray() || isPrimitive());
192+
return *PrimT;
193+
}
194+
186195
/// Returns the allocated size, including metadata.
187196
unsigned getAllocSize() const { return AllocSize; }
188197
/// returns the size of an element when the structure is viewed as an array.

clang/lib/AST/Interp/Interp.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F,
122122
bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
123123
const Pointer &Ptr, const APSInt &IntValue);
124124

125+
/// Copy the contents of Src into Dest.
126+
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest);
127+
125128
/// Checks if the shift operation is legal.
126129
template <typename LT, typename RT>
127130
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) {
14871490
return true;
14881491
}
14891492

1493+
inline bool Memcpy(InterpState &S, CodePtr OpPC) {
1494+
const Pointer &Src = S.Stk.pop<Pointer>();
1495+
Pointer &Dest = S.Stk.peek<Pointer>();
1496+
1497+
if (!CheckLoad(S, OpPC, Src))
1498+
return false;
1499+
1500+
return DoMemcpy(S, OpPC, Src, Dest);
1501+
}
1502+
14901503
//===----------------------------------------------------------------------===//
14911504
// AddOffset, SubOffset
14921505
//===----------------------------------------------------------------------===//

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,5 +1326,50 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
13261326
return true;
13271327
}
13281328

1329+
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
1330+
assert(Src.isLive() && Dest.isLive());
1331+
1332+
const Descriptor *SrcDesc = Src.getFieldDesc();
1333+
const Descriptor *DestDesc = Dest.getFieldDesc();
1334+
1335+
assert(!DestDesc->isPrimitive() && !SrcDesc->isPrimitive());
1336+
1337+
if (DestDesc->isPrimitiveArray()) {
1338+
assert(SrcDesc->isPrimitiveArray());
1339+
assert(SrcDesc->getNumElems() == DestDesc->getNumElems());
1340+
PrimType ET = DestDesc->getPrimType();
1341+
for (unsigned I = 0, N = DestDesc->getNumElems(); I != N; ++I) {
1342+
Pointer DestElem = Dest.atIndex(I);
1343+
TYPE_SWITCH(ET, {
1344+
DestElem.deref<T>() = Src.atIndex(I).deref<T>();
1345+
DestElem.initialize();
1346+
});
1347+
}
1348+
return true;
1349+
}
1350+
1351+
if (DestDesc->isRecord()) {
1352+
assert(SrcDesc->isRecord());
1353+
assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
1354+
const Record *R = DestDesc->ElemRecord;
1355+
for (const Record::Field &F : R->fields()) {
1356+
Pointer DestField = Dest.atField(F.Offset);
1357+
if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
1358+
TYPE_SWITCH(*FT, {
1359+
DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
1360+
DestField.initialize();
1361+
});
1362+
} else {
1363+
return Invalid(S, OpPC);
1364+
}
1365+
}
1366+
return true;
1367+
}
1368+
1369+
// FIXME: Composite types.
1370+
1371+
return Invalid(S, OpPC);
1372+
}
1373+
13291374
} // namespace interp
13301375
} // namespace clang

clang/lib/AST/Interp/Opcodes.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,3 +720,5 @@ def CheckNonNullArg : Opcode {
720720
let Types = [PtrTypeClass];
721721
let HasGroup = 1;
722722
}
723+
724+
def Memcpy : Opcode;

clang/test/AST/Interp/c.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,11 @@ void localCompoundLiteral(void) {
201201
// pedantic-ref-warning {{use of an empty initializer}}
202202
};
203203
}
204+
205+
/// struct copy
206+
struct StrA {int a; };
207+
const struct StrA sa = { 12 };
208+
const struct StrA * const sb = &sa;
209+
const struct StrA sc = *sb;
210+
_Static_assert(sc.a == 12, ""); // pedantic-ref-warning {{GNU extension}} \
211+
// pedantic-expected-warning {{GNU extension}}

0 commit comments

Comments
 (0)