[clang] [clang][Interp] Handle std::move etc. builtins (PR #70772)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 19 02:34:35 PST 2024
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/70772
>From c6c7d246b334a95306cda410e7d115353acb7c5e 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] [clang][Interp] Handle std::move etc. builtins
---
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 | 22 +++++-
clang/test/AST/Interp/functions.cpp | 89 +++++++++++++++++++++++++
clang/test/SemaCXX/builtin-std-move.cpp | 6 +-
6 files changed, 129 insertions(+), 12 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index df4cb736299cb62..907e6f1f764502c 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 ab83a8d13224670..c7620921e467e71 100644
--- a/clang/lib/AST/Interp/Context.h
+++ b/clang/lib/AST/Interp/Context.h
@@ -70,9 +70,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 807b860f3565d3a..38861253b217323 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -141,7 +141,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 754ca96b0c645e5..280aa39398c8e9a 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -634,12 +634,23 @@ 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) {
+
+ 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();
+}
+
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call) {
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());
@@ -848,6 +859,15 @@ 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:
+ case Builtin::BImove_if_noexcept:
+ 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 75f3c5d192b2cf2..6e995ce704e3949 100644
--- a/clang/test/AST/Interp/functions.cpp
+++ b/clang/test/AST/Interp/functions.cpp
@@ -413,3 +413,92 @@ 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 {
+#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, "");
+}
diff --git a/clang/test/SemaCXX/builtin-std-move.cpp b/clang/test/SemaCXX/builtin-std-move.cpp
index e20bf8e156e193b..a2ae21986308a33 100644
--- a/clang/test/SemaCXX/builtin-std-move.cpp
+++ b/clang/test/SemaCXX/builtin-std-move.cpp
@@ -1,6 +1,10 @@
// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s
// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s -DNO_CONSTEXPR
// RUN: %clang_cc1 -std=c++20 -verify=cxx20,expected %s
+//
+// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s -fexperimental-new-constant-interpreter -DNEW_INTERP
+// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s -fexperimental-new-constant-interpreter -DNEW_INTERP -DNO_CONSTEXPR
+// RUN: %clang_cc1 -std=c++20 -verify=cxx20,expected %s -fexperimental-new-constant-interpreter -DNEW_INTERP
namespace std {
#ifndef NO_CONSTEXPR
@@ -112,7 +116,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