tbaeder created this revision.
tbaeder added reviewers: aaron.ballman, erichkeane, tahonermann, shafik.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
Unify `CXXTemporaryObjectExpr` and `CXXConstructExpr` code and zero-initialize
the object if requested.
Hints on how to test this properly without creating some weird intermediate
object are welcome of course. :)
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D154189
Files:
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
Index: clang/test/AST/Interp/records.cpp
===================================================================
--- clang/test/AST/Interp/records.cpp
+++ clang/test/AST/Interp/records.cpp
@@ -899,3 +899,45 @@
return bp.first && bp.second;
}
static_assert(BPand(BoolPair{true, false}) == false, "");
+
+
+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);
+ }
+#if __cplusplus >= 201703L
+ static_assert(foo(A()) == 0, "");
+#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 object outside its lifetime}} \
+ // 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
+
+
+}
+
Index: clang/lib/AST/Interp/Descriptor.h
===================================================================
--- clang/lib/AST/Interp/Descriptor.h
+++ clang/lib/AST/Interp/Descriptor.h
@@ -137,6 +137,7 @@
bool IsTemporary, bool IsMutable);
QualType getType() const;
+ QualType getElemQualType() const;
SourceLocation getLocation() const;
const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
Index: clang/lib/AST/Interp/Descriptor.cpp
===================================================================
--- clang/lib/AST/Interp/Descriptor.cpp
+++ clang/lib/AST/Interp/Descriptor.cpp
@@ -278,6 +278,16 @@
llvm_unreachable("Invalid descriptor type");
}
+QualType Descriptor::getElemQualType() const {
+ assert(isArray());
+ QualType T = getType();
+
+ const auto *CAT = cast<ConstantArrayType>(T);
+ return CAT->getElementType();
+
+ return T;
+}
+
SourceLocation Descriptor::getLocation() const {
if (auto *D = Source.dyn_cast<const Decl *>())
return D->getLocation();
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -92,7 +92,6 @@
bool VisitExprWithCleanups(const ExprWithCleanups *E);
bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *E);
- bool VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *E);
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
bool VisitTypeTraitExpr(const TypeTraitExpr *E);
bool VisitLambdaExpr(const LambdaExpr *E);
@@ -210,6 +209,7 @@
/// 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.
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1019,24 +1019,6 @@
return this->visit(E->getSubExpr());
}
-template <class Emitter>
-bool ByteCodeExprGen<Emitter>::VisitCXXTemporaryObjectExpr(
- const CXXTemporaryObjectExpr *E) {
- if (std::optional<unsigned> LocalIndex =
- allocateLocal(E, /*IsExtended=*/false)) {
- if (!this->emitGetPtrLocal(*LocalIndex, E))
- return false;
-
- if (!this->visitInitializer(E))
- return false;
-
- if (DiscardResult)
- return this->emitPopPtr(E);
- return true;
- }
- return false;
-}
-
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitCompoundLiteralExpr(
const CompoundLiteralExpr *E) {
@@ -1119,19 +1101,30 @@
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitCXXConstructExpr(
const CXXConstructExpr *E) {
- if (std::optional<unsigned> GI = allocateLocal(E, /*IsExtended=*/false)) {
- if (!this->emitGetPtrLocal(*GI, E))
- return false;
+ std::optional<unsigned> LocalIndex = allocateLocal(E, /*IsExtended=*/false);
+ if (!LocalIndex)
+ return false;
- if (!this->visitRecordInitializer(E))
- return false;
+ if (!this->emitGetPtrLocal(*LocalIndex, E))
+ return false;
- if (DiscardResult)
- return this->emitPopPtr(E);
- return true;
+ const CXXConstructorDecl *Ctor = E->getConstructor();
+
+ // If zero-init is requested and the constructor is trivial, zero-init
+ // all field and bases explicitly.
+ if (E->requiresZeroInitialization() && Ctor->isTrivial()) {
+ assert(E->getType()->isRecordType());
+ const Record *R = getRecord(E->getType());
+
+ if (!this->visitZeroRecordInitializer(R, E))
+ return false;
+ } else {
+ // Otherwise, just call the constructor.
+ if (!this->visitInitializer(E))
+ return false;
}
- return false;
+ return DiscardResult ? this->emitPopPtr(E) : true;
}
template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
@@ -1240,6 +1233,48 @@
}
llvm_unreachable("unknown primitive type");
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitZeroRecordInitializer(const Record *R,
+ const Expr *E) {
+ // 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 {
+ // FIXME: Implement class and composite array types.
+ assert(false);
+ }
+
+ if (!this->emitPopPtr(E))
+ return false;
+ }
+
+ return true;
+}
template <class Emitter>
bool ByteCodeExprGen<Emitter>::dereference(
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits