[clang] [clang][Interp] Handle std::move etc. builtins (PR #70772)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 15 00:36:01 PST 2024


Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/70772 at github.com>


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/70772

>From e850b96306ab5d9e6aac4171150195ea013f6ec2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 31 Oct 2023 07:17:16 +0100
Subject: [PATCH 1/5] [clang][Interp] Handle std::move etc. builtins

---
 clang/lib/AST/Interp/InterpBuiltin.cpp | 17 +++++++++++++++++
 clang/test/AST/Interp/functions.cpp    | 15 +++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index 754ca96b0c645e..142f92ffc337c3 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -634,6 +634,15 @@ static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC,
   return true;
 }
 
+static bool interp__builtin_move(InterpState &S, CodePtr OpPC,
+                                 const InterpFrame *Frame, const Function *Func,
+                                 const CallExpr *Call) {
+
+  const Pointer &Arg = S.Stk.peek<Pointer>();
+  S.Stk.push<Pointer>(Arg);
+  return Func->getDecl()->isConstexpr();
+}
+
 bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
                       const CallExpr *Call) {
   InterpFrame *Frame = S.Current;
@@ -848,6 +857,14 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
       return false;
     break;
 
+  case Builtin::BIas_const:
+  case Builtin::BIforward:
+  case Builtin::BIforward_like:
+  case Builtin::BImove:
+    if (!interp__builtin_move(S, OpPC, Frame, F, Call))
+      return false;
+    break;
+
   default:
     return false;
   }
diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp
index 75f3c5d192b2cf..019af555c347a6 100644
--- a/clang/test/AST/Interp/functions.cpp
+++ b/clang/test/AST/Interp/functions.cpp
@@ -413,3 +413,18 @@ namespace AddressOf {
   constexpr _Complex float F = {3, 4};
   static_assert(__builtin_addressof(F) == &F, "");
 }
+
+namespace std {
+template <typename T> struct remove_reference { using type = T; };
+template <typename T> struct remove_reference<T &> { using type = T; };
+template <typename T> struct remove_reference<T &&> { using type = T; };
+template <typename T>
+constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
+  return static_cast<typename std::remove_reference<T>::type &&>(t);
+}
+}
+/// The std::move declaration above gets translated to a builtin function.
+namespace Move {
+  constexpr int A = std::move(5);
+  static_assert(A == 5, "");
+}

>From d90032c82863ea20e7896b78c1d9b78590603a15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 2 Nov 2023 09:17:41 +0100
Subject: [PATCH 2/5] Add more tests

---
 clang/test/AST/Interp/functions.cpp | 74 +++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp
index 019af555c347a6..6e995ce704e394 100644
--- a/clang/test/AST/Interp/functions.cpp
+++ b/clang/test/AST/Interp/functions.cpp
@@ -425,6 +425,80 @@ constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
 }
 /// The std::move declaration above gets translated to a builtin function.
 namespace Move {
+#if __cplusplus >= 202002L
+  consteval int f_eval() { // expected-note 12{{declared here}} \
+                           // ref-note 12{{declared here}}
+    return 0;
+  }
+
+  /// From test/SemaCXX/cxx2a-consteval.
+  struct Copy {
+    int(*ptr)();
+    constexpr Copy(int(*p)() = nullptr) : ptr(p) {}
+    consteval Copy(const Copy&) = default;
+  };
+
+  constexpr const Copy &to_lvalue_ref(const Copy &&a) {
+    return a;
+  }
+
+  void test() {
+    constexpr const Copy C;
+    // there is no the copy constructor call when its argument is a prvalue because of garanteed copy elision.
+    // so we need to test with both prvalue and xvalues.
+    { Copy c(C); }
+    { Copy c((Copy(&f_eval))); } // expected-error {{cannot take address of consteval}} \
+                                 // ref-error {{cannot take address of consteval}}
+    { Copy c(std::move(C)); }
+    { Copy c(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
+                                          // expected-note {{to a consteval}} \
+                                          // ref-error {{is not a constant expression}} \
+                                          // ref-note {{to a consteval}}
+    { Copy c(to_lvalue_ref((Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
+                                                // expected-note {{to a consteval}} \
+                                                // ref-error {{is not a constant expression}} \
+                                                // ref-note {{to a consteval}}
+    { Copy c(to_lvalue_ref(std::move(C))); }
+    { Copy c(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
+                                                         // expected-note {{to a consteval}} \
+                                                         // ref-error {{is not a constant expression}} \
+                                                         // ref-note {{to a consteval}}
+    { Copy c = Copy(C); }
+    { Copy c = Copy(Copy(&f_eval)); } // expected-error {{cannot take address of consteval}} \
+                                      // ref-error {{cannot take address of consteval}}
+    { Copy c = Copy(std::move(C)); }
+    { Copy c = Copy(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
+                                                 // expected-note {{to a consteval}} \
+                                                 // ref-error {{is not a constant expression}} \
+                                                 // ref-note {{to a consteval}}
+    { Copy c = Copy(to_lvalue_ref(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
+                                                     // expected-note {{to a consteval}} \
+                                                     // ref-error {{is not a constant expression}} \
+                                                     // ref-note {{to a consteval}}
+    { Copy c = Copy(to_lvalue_ref(std::move(C))); }
+    { Copy c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
+                                                                // expected-note {{to a consteval}} \
+                                                                // ref-error {{is not a constant expression}} \
+                                                                // ref-note {{to a consteval}}
+    { Copy c; c = Copy(C); }
+    { Copy c; c = Copy(Copy(&f_eval)); } // expected-error {{cannot take address of consteval}} \
+                                         // ref-error {{cannot take address of consteval}}
+    { Copy c; c = Copy(std::move(C)); }
+    { Copy c; c = Copy(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
+                                                    // expected-note {{to a consteval}} \
+                                                    // ref-error {{is not a constant expression}} \
+                                                    // ref-note {{to a consteval}}
+    { Copy c; c = Copy(to_lvalue_ref(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
+                                                        // expected-note {{to a consteval}} \
+                                                        // ref-error {{is not a constant expression}} \
+                                                        // ref-note {{to a consteval}}
+    { Copy c; c = Copy(to_lvalue_ref(std::move(C))); }
+    { Copy c; c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
+                                                                   // expected-note {{to a consteval}} \
+                                                                   // ref-error {{is not a constant expression}} \
+                                                                   // ref-note {{to a consteval}}
+  }
+#endif
   constexpr int A = std::move(5);
   static_assert(A == 5, "");
 }

>From 3c6a990b75c67a4cc143f251da0ea42adf018bf0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 11 Dec 2023 15:08:32 +0100
Subject: [PATCH 3/5] [clang][Interp] Don't diagnose undefined functions when
 checking...

... for a potential constant expression. They are not defined now, but
might be defined later when the function is actually called.
---
 clang/lib/AST/Interp/Interp.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 9de0926b9dba9c..4352e78be45bf0 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -383,9 +383,9 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
           << CD->getInheritedConstructor().getConstructor()->getParent();
         S.Note(DiagDecl->getLocation(), diag::note_declared_at);
       } else {
-        // Don't emit anything if the function isn't defined and we're checking
-        // for a constant expression. It might be defined at the point we're
-        // actually calling it.
+        // Don't emit anything if the function isn't defined and we're checking for
+        // a constnat expression. It might be defined at the point we're actually
+        // calling it.
         if (!DiagDecl->isDefined() && S.checkingPotentialConstantExpression())
           return false;
 

>From 4a684b8ef93b4898e1daa5360bf0a045e7822bf6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 15 Jan 2024 07:57:36 +0100
Subject: [PATCH 4/5] Adapt std::move changes after addressof changes

---
 clang/lib/AST/Interp/ByteCodeExprGen.h |  9 +--------
 clang/lib/AST/Interp/Context.h         | 13 ++++++++++++-
 clang/lib/AST/Interp/Interp.cpp        |  2 +-
 clang/lib/AST/Interp/InterpBuiltin.cpp |  8 +++++---
 4 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index bbb13e97e72569..9757f5dd9b8e34 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -128,15 +128,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
   // If the function does not exist yet, it is compiled.
   const Function *getFunction(const FunctionDecl *FD);
 
-  /// Classifies a type.
   std::optional<PrimType> classify(const Expr *E) const {
-    if (E->isGLValue()) {
-      if (E->getType()->isFunctionType())
-        return PT_FnPtr;
-      return PT_Ptr;
-    }
-
-    return classify(E->getType());
+    return Ctx.classify(E);
   }
   std::optional<PrimType> classify(QualType Ty) const {
     return Ctx.classify(Ty);
diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h
index 7649caef224281..0284b7580318bc 100644
--- a/clang/lib/AST/Interp/Context.h
+++ b/clang/lib/AST/Interp/Context.h
@@ -67,9 +67,20 @@ class Context final {
   /// Return the size of T in bits.
   uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); }
 
-  /// Classifies an expression.
+  /// Classifies a type.
   std::optional<PrimType> classify(QualType T) const;
 
+  /// Classifies an expression.
+  std::optional<PrimType> classify(const Expr *E) const {
+    if (E->isGLValue()) {
+      if (E->getType()->isFunctionType())
+        return PT_FnPtr;
+      return PT_Ptr;
+    }
+
+    return classify(E->getType());
+  }
+
   const CXXMethodDecl *
   getOverridingFunction(const CXXRecordDecl *DynamicDecl,
                         const CXXRecordDecl *StaticDecl,
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 4352e78be45bf0..3956eec6cd6c56 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -122,7 +122,7 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
 namespace clang {
 namespace interp {
 static void popArg(InterpState &S, const Expr *Arg) {
-  PrimType Ty = S.getContext().classify(Arg->getType()).value_or(PT_Ptr);
+  PrimType Ty = S.getContext().classify(Arg).value_or(PT_Ptr);
   TYPE_SWITCH(Ty, S.Stk.discard<T>());
 }
 
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index 142f92ffc337c3..ac0a373ede7ac1 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -638,8 +638,10 @@ static bool interp__builtin_move(InterpState &S, CodePtr OpPC,
                                  const InterpFrame *Frame, const Function *Func,
                                  const CallExpr *Call) {
 
-  const Pointer &Arg = S.Stk.peek<Pointer>();
-  S.Stk.push<Pointer>(Arg);
+  PrimType ArgT = S.getContext().classify(Call->getArg(0)).value_or(PT_Ptr);
+
+  TYPE_SWITCH(ArgT, const T &Arg = S.Stk.peek<T>(); S.Stk.push<T>(Arg););
+
   return Func->getDecl()->isConstexpr();
 }
 
@@ -648,7 +650,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
   InterpFrame *Frame = S.Current;
   APValue Dummy;
 
-  std::optional<PrimType> ReturnT = S.getContext().classify(Call->getType());
+  std::optional<PrimType> ReturnT = S.getContext().classify(Call);
 
   // If classify failed, we assume void.
   assert(ReturnT || Call->getType()->isVoidType());

>From f4f4dd022d09dee65b6152d083af1c7fba9c8052 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 15 Jan 2024 09:35:36 +0100
Subject: [PATCH 5/5] Enable SemaCXX/builtin-std-move.cpp as well

---
 clang/lib/AST/Interp/InterpBuiltin.cpp  | 1 +
 clang/test/SemaCXX/builtin-std-move.cpp | 5 ++++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index ac0a373ede7ac1..280aa39398c8e9 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -863,6 +863,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
   case Builtin::BIforward:
   case Builtin::BIforward_like:
   case Builtin::BImove:
+  case Builtin::BImove_if_noexcept:
     if (!interp__builtin_move(S, OpPC, Frame, F, Call))
       return false;
     break;
diff --git a/clang/test/SemaCXX/builtin-std-move.cpp b/clang/test/SemaCXX/builtin-std-move.cpp
index adad66afc2cbd9..647dfbcd6a5bc1 100644
--- a/clang/test/SemaCXX/builtin-std-move.cpp
+++ b/clang/test/SemaCXX/builtin-std-move.cpp
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -std=c++17 -verify %s
+// RUN: %clang_cc1 -std=c++17 -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
 // RUN: %clang_cc1 -std=c++17 -verify %s -DNO_CONSTEXPR
+// RUN: %clang_cc1 -std=c++17 -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s -DNO_CONSTEXPR
 // RUN: %clang_cc1 -std=c++20 -verify %s
+// RUN: %clang_cc1 -std=c++20 -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
 
 namespace std {
 #ifndef NO_CONSTEXPR
@@ -105,7 +108,7 @@ constexpr bool f(A a) { // #f
 
 #ifndef NO_CONSTEXPR
 static_assert(f({}), "should be constexpr");
-#else
+#elif !defined(NEW_INTERP)
 // expected-error@#f {{never produces a constant expression}}
 // expected-note@#call {{}}
 #endif



More information about the cfe-commits mailing list