Skip to content

Commit 24427c9

Browse files
committed
Address comments
1 parent e66f728 commit 24427c9

File tree

6 files changed

+118
-103
lines changed

6 files changed

+118
-103
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3750,6 +3750,17 @@ Trivially relocates ``count`` objects of relocatable, complete type ``T``
37503750
from ``src`` to ``dest`` and returns ``dest``.
37513751
This builtin is used to implement ``std::trivially_relocate``.
37523752
3753+
``__builtin_invoke``
3754+
--------------------
3755+
3756+
**Syntax**:
3757+
3758+
.. code-block:: c++
3759+
3760+
template <class Callee, class... Args>
3761+
decltype(auto) __builtin_invoke(Callee&& callee, Args&&... args);
3762+
3763+
``__builtin_invoke`` is equivalent to ``std::invoke``.
37533764
37543765
``__builtin_preserve_access_index``
37553766
-----------------------------------

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ Non-comprehensive list of changes in this release
301301
different than before.
302302
- Fixed a crash when a VLA with an invalid size expression was used within a
303303
``sizeof`` or ``typeof`` expression. (#GH138444)
304+
- ``__builtin_invoke`` has been added to improve the compile time of ``std::invoke``.
304305

305306
New Compiler Flags
306307
------------------

clang/include/clang/Sema/Sema.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2594,8 +2594,6 @@ class Sema final : public SemaBase {
25942594
SourceLocation BuiltinLoc,
25952595
SourceLocation RParenLoc);
25962596

2597-
ExprResult BuiltinInvoke(CallExpr *TheCall);
2598-
25992597
static StringRef GetFormatStringTypeName(FormatStringType FST);
26002598
static FormatStringType GetFormatStringType(StringRef FormatFlavor);
26012599
static FormatStringType GetFormatStringType(const FormatAttr *Format);

clang/lib/Sema/SemaChecking.cpp

Lines changed: 94 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,99 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) {
22202220
return false;
22212221
}
22222222

2223+
static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) {
2224+
SourceLocation Loc = TheCall->getBeginLoc();
2225+
MutableArrayRef Args(TheCall->getArgs(), TheCall->getNumArgs());
2226+
assert(llvm::none_of(Args, [](Expr *Arg) { return Arg->isTypeDependent(); }));
2227+
2228+
if (Args.size() == 0) {
2229+
S.Diag(TheCall->getBeginLoc(),
2230+
diag::err_typecheck_call_too_few_args_at_least)
2231+
<< /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0
2232+
<< /*is_non_object=*/0 << TheCall->getSourceRange();
2233+
return ExprError();
2234+
}
2235+
2236+
QualType FuncT = Args[0]->getType();
2237+
2238+
if (const auto *MPT = FuncT->getAs<MemberPointerType>()) {
2239+
if (Args.size() < 2) {
2240+
S.Diag(TheCall->getBeginLoc(),
2241+
diag::err_typecheck_call_too_few_args_at_least)
2242+
<< /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1
2243+
<< /*is_non_object=*/0 << TheCall->getSourceRange();
2244+
return ExprError();
2245+
}
2246+
2247+
const Type *MemPtrClass = MPT->getQualifier()->getAsType();
2248+
QualType ObjectT = Args[1]->getType();
2249+
2250+
if (MPT->isMemberDataPointer() && S.checkArgCount(TheCall, 2))
2251+
return ExprError();
2252+
2253+
ExprResult ObjectArg = [&]() -> ExprResult {
2254+
// (1.1): (t1.*f)(t2, ..., tN) when f is a pointer to a member function of
2255+
// a class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
2256+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
2257+
// (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
2258+
// and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
2259+
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
2260+
if (S.Context.hasSameType(QualType(MemPtrClass, 0),
2261+
S.BuiltinRemoveCVRef(ObjectT, Loc)) ||
2262+
S.BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
2263+
S.BuiltinRemoveCVRef(ObjectT, Loc))) {
2264+
return Args[1];
2265+
}
2266+
2267+
// (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of
2268+
// a class T and remove_cvref_t<decltype(t1)> is a specialization of
2269+
// reference_wrapper;
2270+
if (const auto *RD = ObjectT->getAsCXXRecordDecl()) {
2271+
if (RD->isInStdNamespace() &&
2272+
RD->getDeclName().getAsString() == "reference_wrapper") {
2273+
CXXScopeSpec SS;
2274+
IdentifierInfo *GetName = &S.Context.Idents.get("get");
2275+
UnqualifiedId GetID;
2276+
GetID.setIdentifier(GetName, Loc);
2277+
2278+
ExprResult MemExpr = S.ActOnMemberAccessExpr(
2279+
S.getCurScope(), Args[1], Loc, tok::period, SS,
2280+
/*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
2281+
2282+
if (MemExpr.isInvalid())
2283+
return ExprError();
2284+
2285+
return S.ActOnCallExpr(S.getCurScope(), MemExpr.get(), Loc, {}, Loc);
2286+
}
2287+
}
2288+
2289+
// ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a
2290+
// class T and t1 does not satisfy the previous two items;
2291+
2292+
return S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, Args[1]);
2293+
}();
2294+
2295+
if (ObjectArg.isInvalid())
2296+
return ExprError();
2297+
2298+
ExprResult BinOp = S.ActOnBinOp(S.getCurScope(), TheCall->getBeginLoc(),
2299+
tok::periodstar, ObjectArg.get(), Args[0]);
2300+
if (BinOp.isInvalid())
2301+
return ExprError();
2302+
2303+
if (MPT->isMemberDataPointer())
2304+
return BinOp;
2305+
2306+
auto *MemCall = new (S.Context)
2307+
ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
2308+
2309+
return S.ActOnCallExpr(S.getCurScope(), MemCall, TheCall->getBeginLoc(),
2310+
Args.drop_front(2), TheCall->getRParenLoc());
2311+
}
2312+
return S.ActOnCallExpr(S.getCurScope(), Args.front(), TheCall->getBeginLoc(),
2313+
Args.drop_front(), TheCall->getRParenLoc());
2314+
}
2315+
22232316
ExprResult
22242317
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22252318
CallExpr *TheCall) {
@@ -2369,7 +2462,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
23692462
// TheCall will be freed by the smart pointer here, but that's fine, since
23702463
// BuiltinShuffleVector guts it, but then doesn't release it.
23712464
case Builtin::BI__builtin_invoke:
2372-
return BuiltinInvoke(TheCall);
2465+
return BuiltinInvoke(*this, TheCall);
23732466
case Builtin::BI__builtin_prefetch:
23742467
if (BuiltinPrefetch(TheCall))
23752468
return ExprError();
@@ -5408,99 +5501,6 @@ ExprResult Sema::ConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
54085501
RParenLoc, CurFPFeatureOverrides());
54095502
}
54105503

5411-
ExprResult Sema::BuiltinInvoke(CallExpr *TheCall) {
5412-
SourceLocation Loc = TheCall->getBeginLoc();
5413-
auto Args = MutableArrayRef(TheCall->getArgs(), TheCall->getNumArgs());
5414-
assert(llvm::none_of(Args,
5415-
[](Expr *Arg) { return Arg->isTypeDependent(); }));
5416-
5417-
if (Args.size() == 0) {
5418-
Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least)
5419-
<< /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0
5420-
<< /*is_non_object=*/0 << TheCall->getSourceRange();
5421-
return ExprError();
5422-
}
5423-
5424-
auto FuncT = Args[0]->getType();
5425-
5426-
if (auto *MPT = FuncT->getAs<MemberPointerType>()) {
5427-
if (Args.size() < 2) {
5428-
Diag(TheCall->getBeginLoc(),
5429-
diag::err_typecheck_call_too_few_args_at_least)
5430-
<< /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1
5431-
<< /*is_non_object=*/0 << TheCall->getSourceRange();
5432-
return ExprError();
5433-
}
5434-
5435-
auto *MemPtrClass = MPT->getQualifier()->getAsType();
5436-
auto ObjectT = Args[1]->getType();
5437-
5438-
if (MPT->isMemberDataPointer() && checkArgCount(TheCall, 2))
5439-
return ExprError();
5440-
5441-
ExprResult ObjectArg = [&]() -> ExprResult {
5442-
// (1.1): (t1.*f)(t2, …, tN) when f is a pointer to a member function of a
5443-
// class T and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5444-
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5445-
// (1.4): t1.*f when N=1 and f is a pointer to data member of a class T
5446-
// and is_same_v<T, remove_cvref_t<decltype(t1)>> ||
5447-
// is_base_of_v<T, remove_cvref_t<decltype(t1)>> is true;
5448-
if (Context.hasSameType(QualType(MemPtrClass, 0),
5449-
BuiltinRemoveCVRef(ObjectT, Loc)) ||
5450-
BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0),
5451-
BuiltinRemoveCVRef(ObjectT, Loc))) {
5452-
return Args[1];
5453-
}
5454-
5455-
// (t1.get().*f)(t2, …, tN) when f is a pointer to a member function of
5456-
// a class T and remove_cvref_t<decltype(t1)> is a specialization of
5457-
// reference_wrapper;
5458-
if (auto *RD = ObjectT->getAsCXXRecordDecl()) {
5459-
if (RD->isInStdNamespace() &&
5460-
RD->getDeclName().getAsString() == "reference_wrapper") {
5461-
CXXScopeSpec SS;
5462-
IdentifierInfo *GetName = &Context.Idents.get("get");
5463-
UnqualifiedId GetID;
5464-
GetID.setIdentifier(GetName, Loc);
5465-
5466-
auto MemExpr = ActOnMemberAccessExpr(
5467-
getCurScope(), Args[1], Loc, tok::period, SS,
5468-
/*TemplateKWLoc=*/SourceLocation(), GetID, nullptr);
5469-
5470-
if (MemExpr.isInvalid())
5471-
return ExprError();
5472-
5473-
return ActOnCallExpr(getCurScope(), MemExpr.get(), Loc, {}, Loc);
5474-
}
5475-
}
5476-
5477-
// ((*t1).*f)(t2, …, tN) when f is a pointer to a member function of a
5478-
// class T and t1 does not satisfy the previous two items;
5479-
5480-
return ActOnUnaryOp(getCurScope(), Loc, tok::star, Args[1]);
5481-
}();
5482-
5483-
if (ObjectArg.isInvalid())
5484-
return ExprError();
5485-
5486-
auto BinOp = ActOnBinOp(getCurScope(), TheCall->getBeginLoc(),
5487-
tok::periodstar, ObjectArg.get(), Args[0]);
5488-
if (BinOp.isInvalid())
5489-
return ExprError();
5490-
5491-
if (MPT->isMemberDataPointer())
5492-
return BinOp;
5493-
5494-
auto *MemCall = new (Context)
5495-
ParenExpr(SourceLocation(), SourceLocation(), BinOp.get());
5496-
5497-
return ActOnCallExpr(getCurScope(), MemCall, TheCall->getBeginLoc(),
5498-
Args.drop_front(2), TheCall->getRParenLoc());
5499-
}
5500-
return ActOnCallExpr(getCurScope(), Args.front(), TheCall->getBeginLoc(),
5501-
Args.drop_front(), TheCall->getRParenLoc());
5502-
}
5503-
55045504
bool Sema::BuiltinPrefetch(CallExpr *TheCall) {
55055505
unsigned NumArgs = TheCall->getNumArgs();
55065506

clang/test/SemaCXX/builtin-invoke.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -verify -fsyntax-only %s
1+
// RUN: %clang_cc1 -verify -fsyntax-only %s -std=c++20
22

33
void func() { // expected-note {{'func' declared here}}
44
__builtin_invoke(); // expected-error {{too few arguments to function call, expected at least 1, have 0}}
@@ -127,6 +127,12 @@ namespace std {
127127
auto invoke(Args&&... args) -> decltype(__builtin_invoke(args...));
128128
} // namespace std
129129

130+
template <class... Args>
131+
concept invocable = requires(Args... args) { __builtin_invoke(args...); };
132+
133+
static_assert(!invocable<std::reference_wrapper<InvalidSpecialization1>>);
134+
static_assert(!invocable<std::reference_wrapper<InvalidSpecialization2>>);
135+
130136
void test3() {
131137
__builtin_invoke(diagnose_discard); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
132138
__builtin_invoke(no_diagnose_discard);

libcxx/include/__type_traits/invoke.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ inline const bool __is_nothrow_invocable_r_v =
120120
// is_invocable
121121

122122
template <class _Fn, class... _Args>
123-
struct _LIBCPP_TEMPLATE_VIS is_invocable : bool_constant<__is_invocable_v<_Fn, _Args...> > {};
123+
struct is_invocable : bool_constant<__is_invocable_v<_Fn, _Args...> > {};
124124

125125
template <class _Ret, class _Fn, class... _Args>
126-
struct _LIBCPP_TEMPLATE_VIS is_invocable_r : bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {};
126+
struct is_invocable_r : bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {};
127127

128128
template <class _Fn, class... _Args>
129129
inline constexpr bool is_invocable_v = __is_invocable_v<_Fn, _Args...>;
@@ -134,11 +134,10 @@ inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::va
134134
// is_nothrow_invocable
135135

136136
template <class _Fn, class... _Args>
137-
struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable : bool_constant<__is_nothrow_invocable_v<_Fn, _Args...> > {};
137+
struct is_nothrow_invocable : bool_constant<__is_nothrow_invocable_v<_Fn, _Args...> > {};
138138

139139
template <class _Ret, class _Fn, class... _Args>
140-
struct _LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r
141-
: integral_constant<bool, __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>> {};
140+
struct is_nothrow_invocable_r : integral_constant<bool, __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>> {};
142141

143142
template <class _Fn, class... _Args>
144143
inline constexpr bool is_nothrow_invocable_v = __is_nothrow_invocable_v<_Fn, _Args...>;
@@ -147,7 +146,7 @@ template <class _Ret, class _Fn, class... _Args>
147146
inline constexpr bool is_nothrow_invocable_r_v = __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>;
148147

149148
template <class _Fn, class... _Args>
150-
struct _LIBCPP_TEMPLATE_VIS invoke_result {
149+
struct invoke_result {
151150
using type = __invoke_result_t<_Fn, _Args...>;
152151
};
153152

0 commit comments

Comments
 (0)