[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