[clang][Interp] Materializing primitive temporaries
authorTimm Bäder <tbaeder@redhat.com>
Sat, 15 Oct 2022 12:40:23 +0000 (14:40 +0200)
committerTimm Bäder <tbaeder@redhat.com>
Fri, 3 Feb 2023 14:11:15 +0000 (15:11 +0100)
Implement MaterializeTemporaryExpr for primitive types.

Differential Revision: https://reviews.llvm.org/D136017

clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Opcodes.td
clang/test/AST/Interp/references.cpp

index 4c3b6f5..b02e3ae 100644 (file)
@@ -782,6 +782,57 @@ bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator(
   return this->emitStore(*ResultT, E);
 }
 
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitExprWithCleanups(
+    const ExprWithCleanups *E) {
+  const Expr *SubExpr = E->getSubExpr();
+
+  assert(E->getNumObjects() == 0 && "TODO: Implement cleanups");
+  return this->visit(SubExpr);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitMaterializeTemporaryExpr(
+    const MaterializeTemporaryExpr *E) {
+  StorageDuration SD = E->getStorageDuration();
+
+  // We conservatively only support these for now.
+  if (SD != SD_Static && SD != SD_Automatic)
+    return false;
+
+  const Expr *SubExpr = E->getSubExpr();
+  std::optional<PrimType> SubExprT = classify(SubExpr);
+  // FIXME: Implement this for records and arrays as well.
+  if (!SubExprT)
+    return false;
+
+  if (SD == SD_Static) {
+    if (std::optional<unsigned> GlobalIndex = P.createGlobal(E)) {
+      const LifetimeExtendedTemporaryDecl *TempDecl =
+          E->getLifetimeExtendedTemporaryDecl();
+
+      if (!this->visitInitializer(SubExpr))
+        return false;
+
+      if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E))
+        return false;
+      return this->emitGetPtrGlobal(*GlobalIndex, E);
+    }
+  } else if (SD == SD_Automatic) {
+    if (std::optional<unsigned> LocalIndex =
+            allocateLocalPrimitive(SubExpr, *SubExprT, true, true)) {
+      if (!this->visitInitializer(SubExpr))
+        return false;
+
+      if (!this->emitSetLocal(*SubExprT, *LocalIndex, E))
+        return false;
+      return this->emitGetPtrLocal(*LocalIndex, E);
+    }
+  }
+
+  return false;
+}
+
 template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
   if (E->containsErrors())
     return false;
index 0a64ff3..540b9ec 100644 (file)
@@ -87,6 +87,8 @@ public:
   bool VisitCharacterLiteral(const CharacterLiteral *E);
   bool VisitCompoundAssignOperator(const CompoundAssignOperator *E);
   bool VisitFloatCompoundAssignOperator(const CompoundAssignOperator *E);
+  bool VisitExprWithCleanups(const ExprWithCleanups *E);
+  bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
 
 protected:
   bool visitExpr(const Expr *E) override;
index b35cc01..99c1bd5 100644 (file)
@@ -818,6 +818,22 @@ bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
   return true;
 }
 
+/// 1) Converts the value on top of the stack to an APValue
+/// 2) Sets that APValue on \Temp
+/// 3) Initialized global with index \I with that
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
+                    const LifetimeExtendedTemporaryDecl *Temp) {
+  assert(Temp);
+  const T Value = S.Stk.peek<T>();
+  APValue APV = Value.toAPValue();
+  APValue *Cached = Temp->getOrCreateValue(true);
+  *Cached = APV;
+
+  S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
+  return true;
+}
+
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
   if (S.checkingPotentialConstantExpression())
index 8b49fb4..d1ad7ad 100644 (file)
@@ -49,6 +49,7 @@ def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; }
 def ArgRecordField : ArgType { let Name = "const Record::Field *"; }
 def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; }
 def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; }
+def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; }
 
 //===----------------------------------------------------------------------===//
 // Classes of types instructions operate on.
@@ -332,6 +333,10 @@ def GetGlobal : AccessOpcode;
 // [Value] -> []
 def InitGlobal : AccessOpcode;
 // [Value] -> []
+def InitGlobalTemp : AccessOpcode {
+  let Args = [ArgUint32, ArgLETD];
+}
+// [Value] -> []
 def SetGlobal : AccessOpcode;
 
 // [] -> [Value]
index 0e6d553..5dc6067 100644 (file)
@@ -2,8 +2,6 @@
 // RUN: %clang_cc1 -verify=ref %s
 
 
-// ref-no-diagnostics
-
 constexpr int a = 10;
 constexpr const int &b = a;
 static_assert(a == b, "");
@@ -71,9 +69,22 @@ constexpr int testGetValue() {
 }
 static_assert(testGetValue() == 30, "");
 
-// FIXME: ExprWithCleanups + MaterializeTemporaryExpr not implemented
-constexpr const int &MCE = 1; // expected-error{{must be initialized by a constant expression}}
+constexpr const int &MCE = 20;
+static_assert(MCE == 20, "");
+static_assert(MCE == 30, ""); // expected-error {{static assertion failed}} \
+                              // expected-note {{evaluates to '20 == 30'}} \
+                              // ref-error {{static assertion failed}} \
+                              // ref-note {{evaluates to '20 == 30'}}
 
+constexpr int LocalMCE() {
+  const int &m = 100;
+  return m;
+}
+static_assert(LocalMCE() == 100, "");
+static_assert(LocalMCE() == 200, ""); // expected-error {{static assertion failed}} \
+                                      // expected-note {{evaluates to '100 == 200'}} \
+                                      // ref-error {{static assertion failed}} \
+                                      // ref-note {{evaluates to '100 == 200'}}
 
 struct S {
   int i, j;