diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 0ba301f438d04..a42a546555716 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3798,6 +3798,17 @@ Trivially relocates ``count`` objects of relocatable, complete type ``T`` from ``src`` to ``dest`` and returns ``dest``. This builtin is used to implement ``std::trivially_relocate``. +``__builtin_invoke`` +-------------------- + +**Syntax**: + +.. code-block:: c++ + + template + decltype(auto) __builtin_invoke(Callee&& callee, Args&&... args); + +``__builtin_invoke`` is equivalent to ``std::invoke``. ``__builtin_preserve_access_index`` ----------------------------------- diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index d9847fadc21e5..bee71cf296ac3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -327,6 +327,7 @@ Non-comprehensive list of changes in this release different than before. - Fixed a crash when a VLA with an invalid size expression was used within a ``sizeof`` or ``typeof`` expression. (#GH138444) +- ``__builtin_invoke`` has been added to improve the compile time of ``std::invoke``. - Deprecation warning is emitted for the deprecated ``__reference_binds_to_temporary`` intrinsic. ``__reference_constructs_from_temporary`` should be used instead. (#GH44056) - Added `__builtin_get_vtable_pointer` to directly load the primary vtable pointer from a @@ -656,7 +657,7 @@ Improvements to Clang's diagnostics false positives in exception-heavy code, though only simple patterns are currently recognized. - + Improvements to Clang's time-trace ---------------------------------- @@ -734,7 +735,7 @@ Bug Fixes in This Version - Fixed incorrect token location when emitting diagnostics for tokens expanded from macros. (#GH143216) - Fixed an infinite recursion when checking constexpr destructors. (#GH141789) - Fixed a crash when a malformed using declaration appears in a ``constexpr`` function. (#GH144264) -- Fixed a bug when use unicode character name in macro concatenation. (#GH145240) +- Fixed a bug when use unicode character name in macro concatenation. (#GH145240) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 24810292d1d55..5ebb82180521d 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4314,6 +4314,12 @@ def MoveIfNsoexcept : CxxLibBuiltin<"utility"> { let Namespace = "std"; } +def Invoke : Builtin { + let Spellings = ["__builtin_invoke"]; + let Attributes = [CustomTypeChecking, Constexpr]; + let Prototype = "void(...)"; +} + def Annotation : Builtin { let Spellings = ["__builtin_annotation"]; let Attributes = [NoThrow, CustomTypeChecking]; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9afe3c7710476..3fe26f950ad51 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15192,11 +15192,18 @@ class Sema final : public SemaBase { SourceLocation Loc); QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind, SourceLocation Loc); + + QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) { + return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc); + } + QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind, SourceLocation Loc); QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind, SourceLocation Loc); + bool BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, QualType RhsT); + /// Ensure that the type T is a literal type. /// /// This routine checks whether the type @p T is a literal type. If @p T is an diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 926adebf7de0f..51b22a5e122e4 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2261,6 +2261,99 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) { return false; } +static ExprResult BuiltinInvoke(Sema &S, CallExpr *TheCall) { + SourceLocation Loc = TheCall->getBeginLoc(); + MutableArrayRef Args(TheCall->getArgs(), TheCall->getNumArgs()); + assert(llvm::none_of(Args, [](Expr *Arg) { return Arg->isTypeDependent(); })); + + if (Args.size() == 0) { + S.Diag(TheCall->getBeginLoc(), + diag::err_typecheck_call_too_few_args_at_least) + << /*callee_type=*/0 << /*min_arg_count=*/1 << /*actual_arg_count=*/0 + << /*is_non_object=*/0 << TheCall->getSourceRange(); + return ExprError(); + } + + QualType FuncT = Args[0]->getType(); + + if (const auto *MPT = FuncT->getAs()) { + if (Args.size() < 2) { + S.Diag(TheCall->getBeginLoc(), + diag::err_typecheck_call_too_few_args_at_least) + << /*callee_type=*/0 << /*min_arg_count=*/2 << /*actual_arg_count=*/1 + << /*is_non_object=*/0 << TheCall->getSourceRange(); + return ExprError(); + } + + const Type *MemPtrClass = MPT->getQualifier()->getAsType(); + QualType ObjectT = Args[1]->getType(); + + if (MPT->isMemberDataPointer() && S.checkArgCount(TheCall, 2)) + return ExprError(); + + ExprResult ObjectArg = [&]() -> ExprResult { + // (1.1): (t1.*f)(t2, ..., tN) when f is a pointer to a member function of + // a class T and is_same_v> || + // is_base_of_v> is true; + // (1.4): t1.*f when N=1 and f is a pointer to data member of a class T + // and is_same_v> || + // is_base_of_v> is true; + if (S.Context.hasSameType(QualType(MemPtrClass, 0), + S.BuiltinRemoveCVRef(ObjectT, Loc)) || + S.BuiltinIsBaseOf(Args[1]->getBeginLoc(), QualType(MemPtrClass, 0), + S.BuiltinRemoveCVRef(ObjectT, Loc))) { + return Args[1]; + } + + // (t1.get().*f)(t2, ..., tN) when f is a pointer to a member function of + // a class T and remove_cvref_t is a specialization of + // reference_wrapper; + if (const auto *RD = ObjectT->getAsCXXRecordDecl()) { + if (RD->isInStdNamespace() && + RD->getDeclName().getAsString() == "reference_wrapper") { + CXXScopeSpec SS; + IdentifierInfo *GetName = &S.Context.Idents.get("get"); + UnqualifiedId GetID; + GetID.setIdentifier(GetName, Loc); + + ExprResult MemExpr = S.ActOnMemberAccessExpr( + S.getCurScope(), Args[1], Loc, tok::period, SS, + /*TemplateKWLoc=*/SourceLocation(), GetID, nullptr); + + if (MemExpr.isInvalid()) + return ExprError(); + + return S.ActOnCallExpr(S.getCurScope(), MemExpr.get(), Loc, {}, Loc); + } + } + + // ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a + // class T and t1 does not satisfy the previous two items; + + return S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, Args[1]); + }(); + + if (ObjectArg.isInvalid()) + return ExprError(); + + ExprResult BinOp = S.ActOnBinOp(S.getCurScope(), TheCall->getBeginLoc(), + tok::periodstar, ObjectArg.get(), Args[0]); + if (BinOp.isInvalid()) + return ExprError(); + + if (MPT->isMemberDataPointer()) + return BinOp; + + auto *MemCall = new (S.Context) + ParenExpr(SourceLocation(), SourceLocation(), BinOp.get()); + + return S.ActOnCallExpr(S.getCurScope(), MemCall, TheCall->getBeginLoc(), + Args.drop_front(2), TheCall->getRParenLoc()); + } + return S.ActOnCallExpr(S.getCurScope(), Args.front(), TheCall->getBeginLoc(), + Args.drop_front(), TheCall->getRParenLoc()); +} + ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall) { @@ -2420,6 +2513,8 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return BuiltinShuffleVector(TheCall); // TheCall will be freed by the smart pointer here, but that's fine, since // BuiltinShuffleVector guts it, but then doesn't release it. + case Builtin::BI__builtin_invoke: + return BuiltinInvoke(*this, TheCall); case Builtin::BI__builtin_prefetch: if (BuiltinPrefetch(TheCall)) return ExprError(); diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index cb3d9b77ee4dd..c2f0600295e9e 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -1579,6 +1579,58 @@ ExprResult Sema::ActOnTypeTrait(TypeTrait Kind, SourceLocation KWLoc, return BuildTypeTrait(Kind, KWLoc, ConvertedArgs, RParenLoc); } +bool Sema::BuiltinIsBaseOf(SourceLocation RhsTLoc, QualType LhsT, + QualType RhsT) { + // C++0x [meta.rel]p2 + // Base is a base class of Derived without regard to cv-qualifiers or + // Base and Derived are not unions and name the same class type without + // regard to cv-qualifiers. + + const RecordType *lhsRecord = LhsT->getAs(); + const RecordType *rhsRecord = RhsT->getAs(); + if (!rhsRecord || !lhsRecord) { + const ObjCObjectType *LHSObjTy = LhsT->getAs(); + const ObjCObjectType *RHSObjTy = RhsT->getAs(); + if (!LHSObjTy || !RHSObjTy) + return false; + + ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface(); + ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface(); + if (!BaseInterface || !DerivedInterface) + return false; + + if (RequireCompleteType(RhsTLoc, RhsT, + diag::err_incomplete_type_used_in_type_trait_expr)) + return false; + + return BaseInterface->isSuperClassOf(DerivedInterface); + } + + assert(Context.hasSameUnqualifiedType(LhsT, RhsT) == + (lhsRecord == rhsRecord)); + + // Unions are never base classes, and never have base classes. + // It doesn't matter if they are complete or not. See PR#41843 + if (lhsRecord && lhsRecord->getDecl()->isUnion()) + return false; + if (rhsRecord && rhsRecord->getDecl()->isUnion()) + return false; + + if (lhsRecord == rhsRecord) + return true; + + // C++0x [meta.rel]p2: + // If Base and Derived are class types and are different types + // (ignoring possible cv-qualifiers) then Derived shall be a + // complete type. + if (RequireCompleteType(RhsTLoc, RhsT, + diag::err_incomplete_type_used_in_type_trait_expr)) + return false; + + return cast(rhsRecord->getDecl()) + ->isDerivedFrom(cast(lhsRecord->getDecl())); +} + static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs, @@ -1590,58 +1642,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, "Cannot evaluate traits of dependent types"); switch (BTT) { - case BTT_IsBaseOf: { - // C++0x [meta.rel]p2 - // Base is a base class of Derived without regard to cv-qualifiers or - // Base and Derived are not unions and name the same class type without - // regard to cv-qualifiers. - - const RecordType *lhsRecord = LhsT->getAs(); - const RecordType *rhsRecord = RhsT->getAs(); - if (!rhsRecord || !lhsRecord) { - const ObjCObjectType *LHSObjTy = LhsT->getAs(); - const ObjCObjectType *RHSObjTy = RhsT->getAs(); - if (!LHSObjTy || !RHSObjTy) - return false; - - ObjCInterfaceDecl *BaseInterface = LHSObjTy->getInterface(); - ObjCInterfaceDecl *DerivedInterface = RHSObjTy->getInterface(); - if (!BaseInterface || !DerivedInterface) - return false; - - if (Self.RequireCompleteType( - Rhs->getTypeLoc().getBeginLoc(), RhsT, - diag::err_incomplete_type_used_in_type_trait_expr)) - return false; - - return BaseInterface->isSuperClassOf(DerivedInterface); - } - - assert(Self.Context.hasSameUnqualifiedType(LhsT, RhsT) == - (lhsRecord == rhsRecord)); - - // Unions are never base classes, and never have base classes. - // It doesn't matter if they are complete or not. See PR#41843 - if (lhsRecord && lhsRecord->getDecl()->isUnion()) - return false; - if (rhsRecord && rhsRecord->getDecl()->isUnion()) - return false; + case BTT_IsBaseOf: + return Self.BuiltinIsBaseOf(Rhs->getTypeLoc().getBeginLoc(), LhsT, RhsT); - if (lhsRecord == rhsRecord) - return true; - - // C++0x [meta.rel]p2: - // If Base and Derived are class types and are different types - // (ignoring possible cv-qualifiers) then Derived shall be a - // complete type. - if (Self.RequireCompleteType( - Rhs->getTypeLoc().getBeginLoc(), RhsT, - diag::err_incomplete_type_used_in_type_trait_expr)) - return false; - - return cast(rhsRecord->getDecl()) - ->isDerivedFrom(cast(lhsRecord->getDecl())); - } case BTT_IsVirtualBaseOf: { const RecordType *BaseRecord = LhsT->getAs(); const RecordType *DerivedRecord = RhsT->getAs(); diff --git a/clang/test/CodeGenCXX/builtin-invoke.cpp b/clang/test/CodeGenCXX/builtin-invoke.cpp new file mode 100644 index 0000000000000..af66dfd4dae30 --- /dev/null +++ b/clang/test/CodeGenCXX/builtin-invoke.cpp @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s + +extern "C" void* memcpy(void*, const void*, decltype(sizeof(int))); +void func(); + +namespace std { + template + class reference_wrapper { + T* ptr; + + public: + T& get() { return *ptr; } + }; +} // namespace std + +struct Callable { + void operator()() {} + + void func(); +}; + +extern "C" void call1() { + __builtin_invoke(func); + __builtin_invoke(Callable{}); + __builtin_invoke(memcpy, nullptr, nullptr, 0); + + // CHECK: define dso_local void @call1 + // CHECK-NEXT: entry: + // CHECK-NEXT: %ref.tmp = alloca %struct.Callable, align 1 + // CHECK-NEXT: call void @_Z4funcv() + // CHECK-NEXT: call void @_ZN8CallableclEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp) + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 null, ptr align 1 null, i64 0, i1 false) + // CHECK-NEXT: ret void +} + +extern "C" void call_memptr(std::reference_wrapper wrapper) { + __builtin_invoke(&Callable::func, wrapper); + + // CHECK: define dso_local void @call_memptr + // CHECK-NEXT: entry: + // CHECK-NEXT: %wrapper = alloca %"class.std::reference_wrapper", align 8 + // CHECK-NEXT: %coerce.dive = getelementptr inbounds nuw %"class.std::reference_wrapper", ptr %wrapper, i32 0, i32 0 + // CHECK-NEXT: store ptr %wrapper.coerce, ptr %coerce.dive, align 8 + // CHECK-NEXT: %call = call noundef nonnull align 1 dereferenceable(1) ptr @_ZNSt17reference_wrapperI8CallableE3getEv(ptr noundef nonnull align 8 dereferenceable(8) %wrapper) + // CHECK-NEXT: %0 = getelementptr inbounds i8, ptr %call, i64 0 + // CHECK-NEXT: br i1 false, label %memptr.virtual, label %memptr.nonvirtual + // CHECK-EMPTY: + // CHECK-NEXT: memptr.virtual: + // CHECK-NEXT: %vtable = load ptr, ptr %0, align 8 + // CHECK-NEXT: %1 = getelementptr i8, ptr %vtable, i64 sub (i64 ptrtoint (ptr @_ZN8Callable4funcEv to i64), i64 1), !nosanitize !2 + // CHECK-NEXT: %memptr.virtualfn = load ptr, ptr %1, align 8, !nosanitize !2 + // CHECK-NEXT: br label %memptr.end + // CHECK-EMPTY: + // CHECK-NEXT: memptr.nonvirtual: + // CHECK-NEXT: br label %memptr.end + // CHECK-EMPTY: + // CHECK-NEXT: memptr.end: + // CHECK-NEXT: %2 = phi ptr [ %memptr.virtualfn, %memptr.virtual ], [ @_ZN8Callable4funcEv, %memptr.nonvirtual ] + // CHECK-NEXT: call void %2(ptr noundef nonnull align 1 dereferenceable(1) %0) + // CHECK-NEXT: ret void +} diff --git a/clang/test/SemaCXX/builtin-invoke.cpp b/clang/test/SemaCXX/builtin-invoke.cpp new file mode 100644 index 0000000000000..d7694d619c692 --- /dev/null +++ b/clang/test/SemaCXX/builtin-invoke.cpp @@ -0,0 +1,240 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s -std=c++23 + +void func() { // expected-note {{'func' declared here}} + __builtin_invoke(); // expected-error {{too few arguments to function call, expected at least 1, have 0}} +} + +void nfunc() noexcept {} + +struct S {}; +void argfunc(int, S) {} // expected-note {{'argfunc' declared here}} + +struct Callable { + void operator()() {} + + void func() {} + + int var; +}; + +void* malloc(decltype(sizeof(int))); + +template +struct pointer_wrapper { + T* v; + + T& operator*() { + return *v; + } +}; + +namespace std { + template + class reference_wrapper { + T* ptr; + + public: + constexpr reference_wrapper(T& ref) : ptr(&ref) {} + + constexpr T& get() { return *ptr; } + }; + + template + constexpr reference_wrapper ref(T& v) { + return reference_wrapper(v); + } +} // namespace std + +struct InvalidSpecialization1 { + void func() {} + + int var; +}; + +template <> +class std::reference_wrapper { +public: + reference_wrapper(InvalidSpecialization1&) {} +}; + +struct InvalidSpecialization2 { + void func() {} + + int var; +}; + +template <> +class std::reference_wrapper { +public: + reference_wrapper(InvalidSpecialization2&) {} + +private: + InvalidSpecialization2& get(); // expected-note 2 {{declared private here}} +}; + +struct ExplicitObjectParam { + void func(this const ExplicitObjectParam& self) {} +}; + +struct Incomplete; // expected-note 2 {{forward declaration}} +struct Incomplete2; + +void incomplete_by_val_test(Incomplete); + +void incomplete_test(Incomplete& incomplete) { + __builtin_invoke((int (Incomplete2::*)){}, incomplete); // expected-error {{incomplete type 'Incomplete' used in type trait expression}} \ + expected-error {{indirection requires pointer operand ('Incomplete' invalid)}} + __builtin_invoke(incomplete_test, incomplete); + __builtin_invoke(incomplete_by_val_test, incomplete); // expected-error {{argument type 'Incomplete' is incomplete}} +} + +void call() { + __builtin_invoke(func); + __builtin_invoke(nfunc); + static_assert(!noexcept(__builtin_invoke(func))); + static_assert(noexcept(__builtin_invoke(nfunc))); + __builtin_invoke(func, 1); // expected-error {{too many arguments to function call, expected 0, have 1}} + __builtin_invoke(argfunc, 1); // expected-error {{too few arguments to function call, expected 2, have 1}} + __builtin_invoke(Callable{}); + __builtin_invoke(malloc, 0); + __builtin_invoke(__builtin_malloc, 0); // expected-error {{builtin functions must be directly called}} + + // Variadic function + void variadic_func(int, ...); // expected-note {{declared here}} + + __builtin_invoke(variadic_func); // expected-error {{too few arguments to function call, expected at least 1, have 0}} + __builtin_invoke(variadic_func, 1); + __builtin_invoke(variadic_func, 1, 2, 3); + + // static member function + struct StaticMember { + static void func(int); + }; + + __builtin_invoke(StaticMember::func, 1); + StaticMember sm; + __builtin_invoke(sm.func, 1); + + // lambda + __builtin_invoke([] {}); + __builtin_invoke([](int) {}, 1); + + // Member function pointer + __builtin_invoke(&Callable::func); // expected-error {{too few arguments to function call, expected at least 2, have 1}} + __builtin_invoke(&Callable::func, 1); // expected-error {{indirection requires pointer operand ('int' invalid)}} + __builtin_invoke(&Callable::func, Callable{}); + __builtin_invoke(&Callable::func, Callable{}, 1); // expected-error {{too many arguments to function call, expected 0, have 1}} + __builtin_invoke(&ExplicitObjectParam::func, ExplicitObjectParam{}); + + Callable c; + __builtin_invoke(&Callable::func, &c); + __builtin_invoke(&Callable::func, std::ref(c)); + __builtin_invoke(&Callable::func, &c); + __builtin_invoke(&Callable::func, &c, 2); // expected-error {{too many arguments to function call, expected 0, have 1}} + __builtin_invoke(&Callable::func, pointer_wrapper{&c}); + __builtin_invoke(&Callable::func, pointer_wrapper{&c}, 2); // expected-error {{too many arguments to function call, expected 0, have 1}} + + InvalidSpecialization1 is1; + InvalidSpecialization2 is2; + __builtin_invoke(&InvalidSpecialization1::func, std::ref(is1)); // expected-error {{no member named 'get' in 'std::reference_wrapper'}} + __builtin_invoke(&InvalidSpecialization2::func, std::ref(is2)); // expected-error {{'get' is a private member of 'std::reference_wrapper'}} + + // Member data pointer + __builtin_invoke(&Callable::var); // expected-error {{too few arguments to function call, expected at least 2, have 1}} + __builtin_invoke(&Callable::var, 1); // expected-error {{indirection requires pointer operand ('int' invalid)}} + (void)__builtin_invoke(&Callable::var, Callable{}); + __builtin_invoke(&Callable::var, Callable{}, 1); // expected-error {{too many arguments to function call, expected 2, have 3}} + + (void)__builtin_invoke(&Callable::var, &c); + (void)__builtin_invoke(&Callable::var, std::ref(c)); + (void)__builtin_invoke(&Callable::var, &c); + __builtin_invoke(&Callable::var, &c, 2); // expected-error {{too many arguments to function call, expected 2, have 3}} + (void)__builtin_invoke(&Callable::var, pointer_wrapper{&c}); + __builtin_invoke(&Callable::var, pointer_wrapper{&c}, 2); // expected-error {{too many arguments to function call, expected 2, have 3}} + + __builtin_invoke(&InvalidSpecialization1::var, std::ref(is1)); // expected-error {{no member named 'get' in 'std::reference_wrapper'}} + (void)__builtin_invoke(&InvalidSpecialization2::var, std::ref(is2)); // expected-error {{'get' is a private member of 'std::reference_wrapper'}} +} + +[[nodiscard]] int diagnose_discard(); +int no_diagnose_discard(); + +namespace std { + template + auto invoke(Args&&... args) -> decltype(__builtin_invoke(args...)); +} // namespace std + +template +concept invocable = requires(Args... args) { __builtin_invoke(args...); }; + +static_assert(!invocable>); +static_assert(!invocable>); + +void test3() { + __builtin_invoke(diagnose_discard); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + __builtin_invoke(no_diagnose_discard); +} + +template +auto test(T v) { + return __builtin_invoke(v); +} + +auto call2() { + test(call); +} + +template +void func(ClassT& c, FuncT&& func) { + __builtin_invoke(func, c, 1, 2, 3); // expected-error {{too many arguments to function call, expected 0, have 3}} +} + +struct DependentTest { + void func(int, int, int); + void bad_func(); +}; + +void call3() { + DependentTest d; + func(d, &DependentTest::func); + func(d, &DependentTest::bad_func); // expected-note {{requested here}} +} + +constexpr int constexpr_func() { + return 42; +} + +struct ConstexprTestStruct { + int i; + constexpr int func() { + return 55; + } +}; + +// Make sure that constant evaluation works +static_assert([]() { + + ConstexprTestStruct s; + if (__builtin_invoke(&ConstexprTestStruct::func, s) != 55) // [func.requires]/p1.1 + return false; + if (__builtin_invoke(&ConstexprTestStruct::func, std::ref(s)) != 55) // [func.requires]/p1.2 + return false; + if (__builtin_invoke(&ConstexprTestStruct::func, &s) != 55) // [func.requires]/p1.3 + return false; + + s.i = 22; + if (__builtin_invoke(&ConstexprTestStruct::i, s) != 22) // [func.requires]/p1.4 + return false; + if (__builtin_invoke(&ConstexprTestStruct::i, std::ref(s)) != 22) // [func.requires]/p1.5 + return false; + if (__builtin_invoke(&ConstexprTestStruct::i, &s) != 22) // [func.requires]/p1.6 + return false; + + // [func.requires]/p1.7 + if (__builtin_invoke(constexpr_func) != 42) + return false; + if (__builtin_invoke([] { return 34; }) != 34) + return false; + + return true; +}()); diff --git a/libcxx/include/__type_traits/invoke.h b/libcxx/include/__type_traits/invoke.h index ccf86209d2295..e58fad75e9215 100644 --- a/libcxx/include/__type_traits/invoke.h +++ b/libcxx/include/__type_traits/invoke.h @@ -22,6 +22,7 @@ #include <__type_traits/is_same.h> #include <__type_traits/is_void.h> #include <__type_traits/nat.h> +#include <__type_traits/void_t.h> #include <__utility/declval.h> #include <__utility/forward.h> @@ -61,6 +62,112 @@ _LIBCPP_BEGIN_NAMESPACE_STD +#if __has_builtin(__builtin_invoke) + +template +using __invoke_result_t = decltype(__builtin_invoke(std::declval<_Args>()...)); + +template +struct __invoke_result_impl {}; + +template +struct __invoke_result_impl<__void_t<__invoke_result_t<_Args...> >, _Args...> { + using type _LIBCPP_NODEBUG = __invoke_result_t<_Args...>; +}; + +template +using __invoke_result = __invoke_result_impl; + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __invoke_result_t<_Args...> __invoke(_Args&&... __args) + _NOEXCEPT_(noexcept(__builtin_invoke(std::forward<_Args>(__args)...))) { + return __builtin_invoke(std::forward<_Args>(__args)...); +} + +template +inline const bool __is_invocable_impl = false; + +template +inline const bool __is_invocable_impl<__void_t<__invoke_result_t<_Args...> >, _Args...> = true; + +template +inline const bool __is_invocable_v = __is_invocable_impl; + +template +struct __is_invocable : integral_constant > {}; + +template +inline const bool __is_invocable_r_impl = false; + +template +inline const bool __is_invocable_r_impl<_Ret, true, _Args...> = + __is_core_convertible<__invoke_result_t<_Args...>, _Ret>::value || is_void<_Ret>::value; + +template +inline const bool __is_invocable_r_v = __is_invocable_r_impl<_Ret, __is_invocable_v<_Args...>, _Args...>; + +template +inline const bool __is_nothrow_invocable_impl = false; + +template +inline const bool __is_nothrow_invocable_impl = noexcept(__builtin_invoke(std::declval<_Args>()...)); + +template +inline const bool __is_nothrow_invocable_v = __is_nothrow_invocable_impl<__is_invocable_v<_Args...>, _Args...>; + +template +inline const bool __is_nothrow_invocable_r_impl = false; + +template +inline const bool __is_nothrow_invocable_r_impl = + __is_nothrow_core_convertible_v<__invoke_result_t<_Args...>, _Ret> || is_void<_Ret>::value; + +template +inline const bool __is_nothrow_invocable_r_v = + __is_nothrow_invocable_r_impl<__is_nothrow_invocable_v<_Args...>, _Ret, _Args...>; + +# if _LIBCPP_STD_VER >= 17 + +// is_invocable + +template +struct _LIBCPP_NO_SPECIALIZATIONS is_invocable : bool_constant<__is_invocable_v<_Fn, _Args...> > {}; + +template +struct _LIBCPP_NO_SPECIALIZATIONS is_invocable_r : bool_constant<__is_invocable_r_v<_Ret, _Fn, _Args...>> {}; + +template +_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_invocable_v = __is_invocable_v<_Fn, _Args...>; + +template +_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_invocable_r_v = is_invocable_r<_Ret, _Fn, _Args...>::value; + +// is_nothrow_invocable + +template +struct _LIBCPP_NO_SPECIALIZATIONS is_nothrow_invocable : bool_constant<__is_nothrow_invocable_v<_Fn, _Args...> > {}; + +template +struct _LIBCPP_NO_SPECIALIZATIONS is_nothrow_invocable_r + : integral_constant> {}; + +template +_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_nothrow_invocable_v = __is_nothrow_invocable_v<_Fn, _Args...>; + +template +_LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_nothrow_invocable_r_v = + __is_nothrow_invocable_r_v<_Ret, _Fn, _Args...>; + +template +struct _LIBCPP_NO_SPECIALIZATIONS invoke_result : __invoke_result<_Fn, _Args...> {}; + +template +using invoke_result_t = __invoke_result_t<_Fn, _Args...>; + +# endif // _LIBCPP_STD_VER >= 17 + +#else // __has_builtin(__builtin_invoke) + template struct __member_pointer_class_type {}; @@ -211,21 +318,21 @@ struct __nothrow_invokable_r_imp { template static void __test_noexcept(_Tp) _NOEXCEPT; -#ifdef _LIBCPP_CXX03_LANG +# ifdef _LIBCPP_CXX03_LANG static const bool value = false; -#else +# else static const bool value = noexcept(_ThisT::__test_noexcept<_Ret>(std::__invoke(std::declval<_Fp>(), std::declval<_Args>()...))); -#endif +# endif }; template struct __nothrow_invokable_r_imp { -#ifdef _LIBCPP_CXX03_LANG +# ifdef _LIBCPP_CXX03_LANG static const bool value = false; -#else +# else static const bool value = noexcept(std::__invoke(std::declval<_Fp>(), std::declval<_Args>()...)); -#endif +# endif }; template @@ -236,22 +343,6 @@ template using __nothrow_invokable _LIBCPP_NODEBUG = __nothrow_invokable_r_imp<__is_invocable<_Fp, _Args...>::value, true, void, _Fp, _Args...>; -template ::value> -struct __invoke_void_return_wrapper { - template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) { - return std::__invoke(std::forward<_Args>(__args)...); - } -}; - -template -struct __invoke_void_return_wrapper<_Ret, true> { - template - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) { - std::__invoke(std::forward<_Args>(__args)...); - } -}; - template inline const bool __is_invocable_v = __is_invocable<_Func, _Args...>::value; @@ -268,12 +359,7 @@ struct __invoke_result template using __invoke_result_t _LIBCPP_NODEBUG = typename __invoke_result<_Func, _Args...>::type; -template -_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Ret __invoke_r(_Args&&... __args) { - return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...); -} - -#if _LIBCPP_STD_VER >= 17 +# if _LIBCPP_STD_VER >= 17 // is_invocable @@ -311,7 +397,30 @@ struct _LIBCPP_NO_SPECIALIZATIONS invoke_result : __invoke_result<_Fn, _Args...> template using invoke_result_t = typename invoke_result<_Fn, _Args...>::type; -#endif // _LIBCPP_STD_VER >= 17 +# endif // _LIBCPP_STD_VER >= 17 + +#endif // __has_builtin(__builtin_invoke_r) + +template ::value> +struct __invoke_void_return_wrapper { + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static _Ret __call(_Args&&... __args) { + return std::__invoke(std::forward<_Args>(__args)...); + } +}; + +template +struct __invoke_void_return_wrapper<_Ret, true> { + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 static void __call(_Args&&... __args) { + std::__invoke(std::forward<_Args>(__args)...); + } +}; + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Ret __invoke_r(_Args&&... __args) { + return __invoke_void_return_wrapper<_Ret>::__call(std::forward<_Args>(__args)...); +} _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__type_traits/is_core_convertible.h b/libcxx/include/__type_traits/is_core_convertible.h index ca3a346c17cd7..0b8e94e101db1 100644 --- a/libcxx/include/__type_traits/is_core_convertible.h +++ b/libcxx/include/__type_traits/is_core_convertible.h @@ -37,6 +37,15 @@ concept __core_convertible_to = __is_core_convertible<_Tp, _Up>::value; #endif // _LIBCPP_STD_VER >= 20 +template ::value> +inline const bool __is_nothrow_core_convertible_v = false; + +#ifndef _LIBCPP_CXX03_LANG +template +inline const bool __is_nothrow_core_convertible_v<_Tp, _Up, true> = + noexcept(static_cast(0)(static_cast<_Tp (*)() noexcept>(0)())); +#endif + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___TYPE_TRAITS_IS_CORE_CONVERTIBLE_H