Skip to content

Commit

Permalink
[clang][Interp] Implement zero-init of record types
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D154189
  • Loading branch information
tbaederr committed Sep 5, 2023
1 parent f6b6234 commit 6d79f98
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 1 deletion.
81 changes: 80 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,15 @@ bool ByteCodeExprGen<Emitter>::VisitCXXConstructExpr(
assert(!classify(T));

if (T->isRecordType()) {
const Function *Func = getFunction(E->getConstructor());
const CXXConstructorDecl *Ctor = E->getConstructor();

// Trivial zero initialization.
if (E->requiresZeroInitialization() && Ctor->isTrivial()) {
const Record *R = getRecord(E->getType());
return this->visitZeroRecordInitializer(R, E);
}

const Function *Func = getFunction(Ctor);

if (!Func)
return false;
Expand Down Expand Up @@ -1479,6 +1487,77 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(QualType QT,
llvm_unreachable("unknown primitive type");
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitZeroRecordInitializer(const Record *R,
const Expr *E) {
assert(E);
assert(R);
// Fields
for (const Record::Field &Field : R->fields()) {
const Descriptor *D = Field.Desc;
if (D->isPrimitive()) {
QualType QT = D->getType();
PrimType T = classifyPrim(D->getType());
if (!this->visitZeroInitializer(QT, E))
return false;
if (!this->emitInitField(T, Field.Offset, E))
return false;
continue;
}

// TODO: Add GetPtrFieldPop and get rid of this dup.
if (!this->emitDupPtr(E))
return false;
if (!this->emitGetPtrField(Field.Offset, E))
return false;

if (D->isPrimitiveArray()) {
QualType ET = D->getElemQualType();
PrimType T = classifyPrim(ET);
for (uint32_t I = 0, N = D->getNumElems(); I != N; ++I) {
if (!this->visitZeroInitializer(ET, E))
return false;
if (!this->emitInitElem(T, I, E))
return false;
}
} else if (D->isCompositeArray()) {
const Record *ElemRecord = D->ElemDesc->ElemRecord;
assert(D->ElemDesc->ElemRecord);
for (uint32_t I = 0, N = D->getNumElems(); I != N; ++I) {
if (!this->emitConstUint32(I, E))
return false;
if (!this->emitArrayElemPtr(PT_Uint32, E))
return false;
if (!this->visitZeroRecordInitializer(ElemRecord, E))
return false;
if (!this->emitPopPtr(E))
return false;
}
} else if (D->isRecord()) {
if (!this->visitZeroRecordInitializer(D->ElemRecord, E))
return false;
} else {
assert(false);
}

if (!this->emitPopPtr(E))
return false;
}

for (const Record::Base &B : R->bases()) {
if (!this->emitGetPtrBase(B.Offset, E))
return false;
if (!this->visitZeroRecordInitializer(B.R, E))
return false;
if (!this->emitPopPtr(E))
return false;
}

// FIXME: Virtual bases.

return true;
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::dereference(
const Expr *LV, DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,

/// Emits a zero initializer.
bool visitZeroInitializer(QualType QT, const Expr *E);
bool visitZeroRecordInitializer(const Record *R, const Expr *E);

enum class DerefKind {
/// Value is read and pushed to stack.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@ QualType Descriptor::getType() const {
llvm_unreachable("Invalid descriptor type");
}

QualType Descriptor::getElemQualType() const {
assert(isArray());
const auto *AT = cast<ArrayType>(getType());
return AT->getElementType();
}

SourceLocation Descriptor::getLocation() const {
if (auto *D = Source.dyn_cast<const Decl *>())
return D->getLocation();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ struct Descriptor final {
bool IsTemporary, bool IsMutable);

QualType getType() const;
QualType getElemQualType() const;
SourceLocation getLocation() const;

const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
Expand Down
112 changes: 112 additions & 0 deletions clang/test/AST/Interp/records.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -911,3 +911,115 @@ namespace TemporaryObjectExpr {
}
static_assert(foo(F()) == 0, "");
}

namespace ZeroInit {
struct F {
int a;
};

namespace Simple {
struct A {
char a;
bool b;
int c[4];
float d;
};
constexpr int foo(A x) {
return x.a + static_cast<int>(x.b) + x.c[0] + x.c[3] + static_cast<int>(x.d);
}
static_assert(foo(A()) == 0, "");
}

namespace Inheritance {
struct F2 : F {
float f;
};

constexpr int foo(F2 f) {
return (int)f.f + f.a;
}
static_assert(foo(F2()) == 0, "");
}

namespace BitFields {
struct F {
unsigned a : 6;
};
constexpr int foo(F f) {
return f.a;
}
static_assert(foo(F()) == 0, "");
}

namespace Nested {
struct F2 {
float f;
char c;
};

struct F {
F2 f2;
int i;
};

constexpr int foo(F f) {
return f.i + f.f2.f + f.f2.c;
}
static_assert(foo(F()) == 0, "");
}

namespace CompositeArrays {
struct F2 {
float f;
char c;
};

struct F {
F2 f2[2];
int i;
};

constexpr int foo(F f) {
return f.i + f.f2[0].f + f.f2[0].c + f.f2[1].f + f.f2[1].c;
}
static_assert(foo(F()) == 0, "");
}

/// FIXME: This needs support for unions on the new interpreter.
/// We diagnose an uninitialized object in c++14.
#if __cplusplus > 201402L
namespace Unions {
struct F {
union {
int a;
char c[4];
float f;
} U;
int i;
};

constexpr int foo(F f) {
return f.i + f.U.f; // ref-note {{read of member 'f' of union with active member 'a'}}
}
static_assert(foo(F()) == 0, ""); // ref-error {{not an integral constant expression}} \
// ref-note {{in call to}}
}
#endif

#if __cplusplus >= 202002L
namespace Failure {
struct S {
int a;
F f{12};
};
constexpr int foo(S x) {
return x.a; // expected-note {{read of uninitialized object}} \
// ref-note {{read of uninitialized object}}
}
static_assert(foo(S()) == 0, ""); // expected-error {{not an integral constant expression}} \
// expected-note {{in call to}} \
// ref-error {{not an integral constant expression}} \
// ref-note {{in call to}}
};
#endif
}

0 comments on commit 6d79f98

Please sign in to comment.