[clang] 6d79f98 - [clang][Interp] Implement zero-init of record types
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 5 02:22:38 PDT 2023
Author: Timm Bäder
Date: 2023-09-05T11:20:35+02:00
New Revision: 6d79f985b53ef57f670ab7e4c7f59c953b49b4d6
URL: https://github.com/llvm/llvm-project/commit/6d79f985b53ef57f670ab7e4c7f59c953b49b4d6
DIFF: https://github.com/llvm/llvm-project/commit/6d79f985b53ef57f670ab7e4c7f59c953b49b4d6.diff
LOG: [clang][Interp] Implement zero-init of record types
Differential Revision: https://reviews.llvm.org/D154189
Added:
Modified:
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/Descriptor.cpp
clang/lib/AST/Interp/Descriptor.h
clang/test/AST/Interp/records.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 4708799773603d..131525c98ca59c 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -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;
@@ -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,
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index cd924e911759e0..dda954320cd24e 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -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.
diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp
index b4c26ac88b5c6b..db49a569eff33e 100644
--- a/clang/lib/AST/Interp/Descriptor.cpp
+++ b/clang/lib/AST/Interp/Descriptor.cpp
@@ -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();
diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h
index c5b73eca4c2e9f..55a754c3505cce 100644
--- a/clang/lib/AST/Interp/Descriptor.h
+++ b/clang/lib/AST/Interp/Descriptor.h
@@ -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 *>(); }
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 2b79ec8be0311d..7976f2d91e99c0 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -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
+}
More information about the cfe-commits
mailing list