Skip to content

Commit 7f4cb1f

Browse files
committed
[clang][Interp] Handle std::move etc. builtins
1 parent cd0b005 commit 7f4cb1f

File tree

6 files changed

+129
-12
lines changed

6 files changed

+129
-12
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
128128
// If the function does not exist yet, it is compiled.
129129
const Function *getFunction(const FunctionDecl *FD);
130130

131-
/// Classifies a type.
132131
std::optional<PrimType> classify(const Expr *E) const {
133-
if (E->isGLValue()) {
134-
if (E->getType()->isFunctionType())
135-
return PT_FnPtr;
136-
return PT_Ptr;
137-
}
138-
139-
return classify(E->getType());
132+
return Ctx.classify(E);
140133
}
141134
std::optional<PrimType> classify(QualType Ty) const {
142135
return Ctx.classify(Ty);

clang/lib/AST/Interp/Context.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,20 @@ class Context final {
7070
/// Return the size of T in bits.
7171
uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); }
7272

73-
/// Classifies an expression.
73+
/// Classifies a type.
7474
std::optional<PrimType> classify(QualType T) const;
7575

76+
/// Classifies an expression.
77+
std::optional<PrimType> classify(const Expr *E) const {
78+
if (E->isGLValue()) {
79+
if (E->getType()->isFunctionType())
80+
return PT_FnPtr;
81+
return PT_Ptr;
82+
}
83+
84+
return classify(E->getType());
85+
}
86+
7687
const CXXMethodDecl *
7788
getOverridingFunction(const CXXRecordDecl *DynamicDecl,
7889
const CXXRecordDecl *StaticDecl,

clang/lib/AST/Interp/Interp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
141141
namespace clang {
142142
namespace interp {
143143
static void popArg(InterpState &S, const Expr *Arg) {
144-
PrimType Ty = S.getContext().classify(Arg->getType()).value_or(PT_Ptr);
144+
PrimType Ty = S.getContext().classify(Arg).value_or(PT_Ptr);
145145
TYPE_SWITCH(Ty, S.Stk.discard<T>());
146146
}
147147

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,12 +634,23 @@ static bool interp__builtin_addressof(InterpState &S, CodePtr OpPC,
634634
return true;
635635
}
636636

637+
static bool interp__builtin_move(InterpState &S, CodePtr OpPC,
638+
const InterpFrame *Frame, const Function *Func,
639+
const CallExpr *Call) {
640+
641+
PrimType ArgT = S.getContext().classify(Call->getArg(0)).value_or(PT_Ptr);
642+
643+
TYPE_SWITCH(ArgT, const T &Arg = S.Stk.peek<T>(); S.Stk.push<T>(Arg););
644+
645+
return Func->getDecl()->isConstexpr();
646+
}
647+
637648
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
638649
const CallExpr *Call) {
639650
InterpFrame *Frame = S.Current;
640651
APValue Dummy;
641652

642-
std::optional<PrimType> ReturnT = S.getContext().classify(Call->getType());
653+
std::optional<PrimType> ReturnT = S.getContext().classify(Call);
643654

644655
// If classify failed, we assume void.
645656
assert(ReturnT || Call->getType()->isVoidType());
@@ -848,6 +859,15 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
848859
return false;
849860
break;
850861

862+
case Builtin::BIas_const:
863+
case Builtin::BIforward:
864+
case Builtin::BIforward_like:
865+
case Builtin::BImove:
866+
case Builtin::BImove_if_noexcept:
867+
if (!interp__builtin_move(S, OpPC, Frame, F, Call))
868+
return false;
869+
break;
870+
851871
default:
852872
return false;
853873
}

clang/test/AST/Interp/functions.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,92 @@ namespace AddressOf {
413413
constexpr _Complex float F = {3, 4};
414414
static_assert(__builtin_addressof(F) == &F, "");
415415
}
416+
417+
namespace std {
418+
template <typename T> struct remove_reference { using type = T; };
419+
template <typename T> struct remove_reference<T &> { using type = T; };
420+
template <typename T> struct remove_reference<T &&> { using type = T; };
421+
template <typename T>
422+
constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
423+
return static_cast<typename std::remove_reference<T>::type &&>(t);
424+
}
425+
}
426+
/// The std::move declaration above gets translated to a builtin function.
427+
namespace Move {
428+
#if __cplusplus >= 202002L
429+
consteval int f_eval() { // expected-note 12{{declared here}} \
430+
// ref-note 12{{declared here}}
431+
return 0;
432+
}
433+
434+
/// From test/SemaCXX/cxx2a-consteval.
435+
struct Copy {
436+
int(*ptr)();
437+
constexpr Copy(int(*p)() = nullptr) : ptr(p) {}
438+
consteval Copy(const Copy&) = default;
439+
};
440+
441+
constexpr const Copy &to_lvalue_ref(const Copy &&a) {
442+
return a;
443+
}
444+
445+
void test() {
446+
constexpr const Copy C;
447+
// there is no the copy constructor call when its argument is a prvalue because of garanteed copy elision.
448+
// so we need to test with both prvalue and xvalues.
449+
{ Copy c(C); }
450+
{ Copy c((Copy(&f_eval))); } // expected-error {{cannot take address of consteval}} \
451+
// ref-error {{cannot take address of consteval}}
452+
{ Copy c(std::move(C)); }
453+
{ Copy c(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
454+
// expected-note {{to a consteval}} \
455+
// ref-error {{is not a constant expression}} \
456+
// ref-note {{to a consteval}}
457+
{ Copy c(to_lvalue_ref((Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
458+
// expected-note {{to a consteval}} \
459+
// ref-error {{is not a constant expression}} \
460+
// ref-note {{to a consteval}}
461+
{ Copy c(to_lvalue_ref(std::move(C))); }
462+
{ Copy c(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
463+
// expected-note {{to a consteval}} \
464+
// ref-error {{is not a constant expression}} \
465+
// ref-note {{to a consteval}}
466+
{ Copy c = Copy(C); }
467+
{ Copy c = Copy(Copy(&f_eval)); } // expected-error {{cannot take address of consteval}} \
468+
// ref-error {{cannot take address of consteval}}
469+
{ Copy c = Copy(std::move(C)); }
470+
{ Copy c = Copy(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
471+
// expected-note {{to a consteval}} \
472+
// ref-error {{is not a constant expression}} \
473+
// ref-note {{to a consteval}}
474+
{ Copy c = Copy(to_lvalue_ref(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
475+
// expected-note {{to a consteval}} \
476+
// ref-error {{is not a constant expression}} \
477+
// ref-note {{to a consteval}}
478+
{ Copy c = Copy(to_lvalue_ref(std::move(C))); }
479+
{ Copy c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
480+
// expected-note {{to a consteval}} \
481+
// ref-error {{is not a constant expression}} \
482+
// ref-note {{to a consteval}}
483+
{ Copy c; c = Copy(C); }
484+
{ Copy c; c = Copy(Copy(&f_eval)); } // expected-error {{cannot take address of consteval}} \
485+
// ref-error {{cannot take address of consteval}}
486+
{ Copy c; c = Copy(std::move(C)); }
487+
{ Copy c; c = Copy(std::move(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
488+
// expected-note {{to a consteval}} \
489+
// ref-error {{is not a constant expression}} \
490+
// ref-note {{to a consteval}}
491+
{ Copy c; c = Copy(to_lvalue_ref(Copy(&f_eval))); } // expected-error {{is not a constant expression}} \
492+
// expected-note {{to a consteval}} \
493+
// ref-error {{is not a constant expression}} \
494+
// ref-note {{to a consteval}}
495+
{ Copy c; c = Copy(to_lvalue_ref(std::move(C))); }
496+
{ Copy c; c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); } // expected-error {{is not a constant expression}} \
497+
// expected-note {{to a consteval}} \
498+
// ref-error {{is not a constant expression}} \
499+
// ref-note {{to a consteval}}
500+
}
501+
#endif
502+
constexpr int A = std::move(5);
503+
static_assert(A == 5, "");
504+
}

clang/test/SemaCXX/builtin-std-move.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s
22
// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s -DNO_CONSTEXPR
33
// RUN: %clang_cc1 -std=c++20 -verify=cxx20,expected %s
4+
//
5+
// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s -fexperimental-new-constant-interpreter -DNEW_INTERP
6+
// RUN: %clang_cc1 -std=c++17 -verify=cxx17,expected %s -fexperimental-new-constant-interpreter -DNEW_INTERP -DNO_CONSTEXPR
7+
// RUN: %clang_cc1 -std=c++20 -verify=cxx20,expected %s -fexperimental-new-constant-interpreter -DNEW_INTERP
48

59
namespace std {
610
#ifndef NO_CONSTEXPR
@@ -112,7 +116,7 @@ constexpr bool f(A a) { // #f
112116

113117
#ifndef NO_CONSTEXPR
114118
static_assert(f({}), "should be constexpr");
115-
#else
119+
#elif !defined(NEW_INTERP)
116120
// expected-error@#f {{never produces a constant expression}}
117121
// expected-note@#call {{}}
118122
#endif

0 commit comments

Comments
 (0)