Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -260,6 +260,29 @@ not ODR-equivalent. Query for this feature with ``__has_attribute(enable_if)``. + +Note that functions with one or more ``enable_if`` attributes may not have +their address taken, unless all of the conditions specified by said +``enable_if`` are constants that evaluate to ``true``. For example: + +.. code-block:: c + + const int TrueConstant = 1; + const int FalseConstant = 0; + int f(int a) __attribute__((enable_if(a > 0, ""))); + int g(int a) __attribute__((enable_if(a == 0 || a != 0, ""))); + int h(int a) __attribute__((enable_if(1, ""))); + int i(int a) __attribute__((enable_if(TrueConstant, ""))); + int j(int a) __attribute__((enable_if(FalseConstant, ""))); + + void fn() { + int (*ptr)(int); + ptr = &f; // error: 'a > 0' is not always true + ptr = &g; // error: 'a == 0 || a != 0' is not a truthy constant + ptr = &h; // OK -- 1 is a truthy constant + ptr = &i; // OK -- 'TrueConstant' is a truthy constant + ptr = &j; // error: 'FalseConstant' is a constant, but not truthy + } }]; } Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2503,6 +2503,10 @@ DeclAccessPair &Found, bool *pHadMultipleCandidates = nullptr); + bool resolveAndFixAddressOfOnlyViableOverloadCandidate( + ExprResult &SrcExpr, bool Complain = false, + bool *pHadMultipleCandidates = nullptr); + FunctionDecl * ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, bool Complain = false, Index: lib/Sema/SemaCast.cpp =================================================================== --- lib/Sema/SemaCast.cpp +++ lib/Sema/SemaCast.cpp @@ -1750,6 +1750,30 @@ } } +static bool fixOverloadedReinterpretCastExpr(Sema &Self, QualType DestType, + ExprResult &Result) { + // We can only fix an overloaded reinterpret_cast if + // - it is a template with explicit arguments that resolves to an lvalue + // unambiguously, or + // - it is the only function in an overload set that may have its address + // taken. + + ExprResult Saved = Result; + // TODO: what if this fails because of DiagnoseUseOfDecl or something + // like it? + if (Self.ResolveAndFixSingleFunctionTemplateSpecialization( + Result, + Expr::getValueKindForType(DestType) == VK_RValue // Convert Fun to Ptr + ) && + Result.isUsable()) + return true; + + // No guarantees that Result is sane if the above call fails. + Result = Saved; + return Self.resolveAndFixAddressOfOnlyViableOverloadCandidate( + Result, /*Complain=*/false); +} + static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, bool CStyle, SourceRange OpRange, @@ -1761,21 +1785,17 @@ QualType SrcType = SrcExpr.get()->getType(); // Is the source an overloaded name? (i.e. &foo) - // If so, reinterpret_cast can not help us here (13.4, p1, bullet 5) ... + // If so, reinterpret_cast generally can not help us here (13.4, p1, bullet 5) if (SrcType == Self.Context.OverloadTy) { - // ... unless foo resolves to an lvalue unambiguously. - // TODO: what if this fails because of DiagnoseUseOfDecl or something - // like it? - ExprResult SingleFunctionExpr = SrcExpr; - if (Self.ResolveAndFixSingleFunctionTemplateSpecialization( - SingleFunctionExpr, - Expr::getValueKindForType(DestType) == VK_RValue // Convert Fun to Ptr - ) && SingleFunctionExpr.isUsable()) { - SrcExpr = SingleFunctionExpr; - SrcType = SrcExpr.get()->getType(); - } else { + ExprResult FixedExpr = SrcExpr; + if (!fixOverloadedReinterpretCastExpr(Self, DestType, FixedExpr)) return TC_NotApplicable; - } + + assert(FixedExpr.isUsable() && "Invalid result fixing overloaded expr"); + assert(FixedExpr.get()->getType() != Self.Context.OverloadTy && + "Fixing overloaded expr didn't resolve the overload"); + SrcExpr = FixedExpr; + SrcType = SrcExpr.get()->getType(); } if (const ReferenceType *DestTypeTmp = DestType->getAs()) { Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -8484,7 +8484,7 @@ // Cand1's first N enable_if attributes have precisely the same conditions as // Cand2's first N enable_if attributes (where N = the number of enable_if // attributes on Cand2), and Cand1 has more than N enable_if attributes. -static bool hasBetterEnableIfAttrs(Sema &S, const FunctionDecl *Cand1, +static bool hasBetterEnableIfAttrs(const Sema &S, const FunctionDecl *Cand1, const FunctionDecl *Cand2) { // FIXME: The next several lines are just @@ -10211,14 +10211,15 @@ const QualType& TargetType; QualType TargetFunctionType; // Extracted function type from target type - bool Complain; //DeclAccessPair& ResultFunctionAccessPair; ASTContext& Context; + bool Complain; bool TargetTypeIsNonStaticMemberFunction; bool FoundNonTemplateFunction; bool StaticMemberFunctionFromBoundPointer; bool HasComplained; + bool RequireExactType; OverloadExpr::FindResult OvlExprInfo; OverloadExpr *OvlExpr; @@ -10228,14 +10229,16 @@ public: AddressOfFunctionResolver(Sema &S, Expr *SourceExpr, - const QualType &TargetType, bool Complain) + const QualType &TargetType, bool Complain, + bool NeedExactMatch) : S(S), SourceExpr(SourceExpr), TargetType(TargetType), - Complain(Complain), Context(S.getASTContext()), + Context(S.getASTContext()), Complain(Complain), TargetTypeIsNonStaticMemberFunction( !!TargetType->getAs()), FoundNonTemplateFunction(false), StaticMemberFunctionFromBoundPointer(false), HasComplained(false), + RequireExactType(NeedExactMatch), OvlExprInfo(OverloadExpr::find(SourceExpr)), OvlExpr(OvlExprInfo.Expression), FailedCandidates(OvlExpr->getNameLoc(), /*ForTakingAddress=*/true) { @@ -10290,13 +10293,25 @@ bool hasComplained() const { return HasComplained; } private: - // Is A considered a better overload candidate for the desired type than B? + bool candidateHasExactlyCorrectType(const FunctionDecl *FD) { + QualType Discard; + return Context.hasSameUnqualifiedType(TargetFunctionType, FD->getType()) || + S.IsNoReturnConversion(FD->getType(), TargetFunctionType, Discard); + } + + /// \return true if A is considered a better overload candidate for the + /// desired type than B. bool isBetterCandidate(const FunctionDecl *A, const FunctionDecl *B) { - return hasBetterEnableIfAttrs(S, A, B); + // If A doesn't have exactly the correct type, we don't want to classify it + // as "better" than anything else. This way, the user is required to + // disambiguate for us if there are multiple candidates and no exact match. + return candidateHasExactlyCorrectType(A) && + (!candidateHasExactlyCorrectType(B) || + hasBetterEnableIfAttrs(S, A, B)); } - // Returns true if we've eliminated any (read: all but one) candidates, false - // otherwise. + /// \return true if we were able to eliminate all but one overload candidate, + /// false otherwise. bool eliminiateSuboptimalOverloadCandidates() { // Same algorithm as overload resolution -- one pass to pick the "best", // another pass to be sure that nothing is better than the best. @@ -10409,12 +10424,7 @@ if (!S.checkAddressOfFunctionIsAvailable(FunDecl)) return false; - QualType ResultTy; - if (Context.hasSameUnqualifiedType(TargetFunctionType, - FunDecl->getType()) || - S.IsNoReturnConversion(FunDecl->getType(), TargetFunctionType, - ResultTy) || - (!S.getLangOpts().CPlusPlus && TargetType->isVoidPointerType())) { + if (!RequireExactType || candidateHasExactlyCorrectType(FunDecl)) { Matches.push_back(std::make_pair( CurAccessFunPair, cast(FunDecl->getCanonicalDecl()))); FoundNonTemplateFunction = true; @@ -10589,31 +10599,13 @@ }; } -/// ResolveAddressOfOverloadedFunction - Try to resolve the address of -/// an overloaded function (C++ [over.over]), where @p From is an -/// expression with overloaded function type and @p ToType is the type -/// we're trying to resolve to. For example: -/// -/// @code -/// int f(double); -/// int f(int); -/// -/// int (*pfd)(double) = f; // selects f(double) -/// @endcode -/// -/// This routine returns the resulting FunctionDecl if it could be -/// resolved, and NULL otherwise. When @p Complain is true, this -/// routine will emit diagnostics if there is an error. -FunctionDecl * -Sema::ResolveAddressOfOverloadedFunction(Expr *AddressOfExpr, - QualType TargetType, - bool Complain, - DeclAccessPair &FoundResult, - bool *pHadMultipleCandidates) { - assert(AddressOfExpr->getType() == Context.OverloadTy); +static FunctionDecl *resolveAddressOfOverloadedFunction( + Sema &S, Expr *AddressOfExpr, QualType TargetType, bool NeedExactMatch, + bool Complain, DeclAccessPair &FoundResult, bool *pHadMultipleCandidates) { + assert(AddressOfExpr->getType() == S.Context.OverloadTy); - AddressOfFunctionResolver Resolver(*this, AddressOfExpr, TargetType, - Complain); + AddressOfFunctionResolver Resolver(S, AddressOfExpr, TargetType, Complain, + NeedExactMatch); int NumMatches = Resolver.getNumMatches(); FunctionDecl *Fn = nullptr; bool ShouldComplain = Complain && !Resolver.hasComplained(); @@ -10622,8 +10614,7 @@ Resolver.ComplainIsInvalidFormOfPointerToMemberFunction(); else Resolver.ComplainNoMatchesFound(); - } - else if (NumMatches > 1 && ShouldComplain) + } else if (NumMatches > 1 && ShouldComplain) Resolver.ComplainMultipleMatchesFound(); else if (NumMatches == 1) { Fn = Resolver.getMatchingFunctionDecl(); @@ -10633,7 +10624,7 @@ if (Resolver.IsStaticMemberFunctionFromBoundPointer()) Resolver.ComplainIsStaticMemberFunctionFromBoundPointer(); else - CheckAddressOfMemberAccess(AddressOfExpr, FoundResult); + S.CheckAddressOfMemberAccess(AddressOfExpr, FoundResult); } } @@ -10642,6 +10633,60 @@ return Fn; } +/// ResolveAddressOfOverloadedFunction - Try to resolve the address of +/// an overloaded function (C++ [over.over]), where @p From is an +/// expression with overloaded function type and @p ToType is the type +/// we're trying to resolve to. For example: +/// +/// @code +/// int f(double); +/// int f(int); +/// +/// int (*pfd)(double) = f; // selects f(double) +/// @endcode +/// +/// This routine returns the resulting FunctionDecl if it could be +/// resolved, and NULL otherwise. When @p Complain is true, this +/// routine will emit diagnostics if there is an error. +FunctionDecl * +Sema::ResolveAddressOfOverloadedFunction(Expr *AddressOfExpr, + QualType TargetType, + bool Complain, + DeclAccessPair &FoundResult, + bool *pHadMultipleCandidates) { + return ::resolveAddressOfOverloadedFunction( + *this, AddressOfExpr, TargetType, + /*NeedExactMatch=*/getLangOpts().CPlusPlus, Complain, FoundResult, + pHadMultipleCandidates); +} + +/// \brief Given an expression that refers to an overloaded function, try to +/// resolve that function to a single function that can have its address taken. +/// +/// This routine can only realistically succeed if all but one candidates in the +/// overload set for SrcExpr cannot have their addresses taken. +bool Sema::resolveAndFixAddressOfOnlyViableOverloadCandidate( + ExprResult &SrcExpr, bool Complain, bool *pHadMultipleCandidates) { + DeclAccessPair FoundResult; + FunctionDecl *Result = ::resolveAddressOfOverloadedFunction( + *this, SrcExpr.get(), Context.VoidPtrTy, /*NeedExactMatch=*/false, + Complain, FoundResult, pHadMultipleCandidates); + + if (!Result) + return false; + + Expr *E = FixOverloadedFunctionReference(SrcExpr.get(), FoundResult, Result); + if (E->getType()->isFunctionType()) { + ExprResult Conv = DefaultFunctionArrayConversion(E, /*Diagnose=*/false); + if (Conv.isInvalid()) + return false; + E = Conv.get(); + } + + SrcExpr = E; + return true; +} + /// \brief Given an expression that refers to an overloaded function, try to /// resolve that overloaded function expression down to a single function. /// Index: test/CodeGen/overloadable.c =================================================================== --- test/CodeGen/overloadable.c +++ test/CodeGen/overloadable.c @@ -29,3 +29,33 @@ cdv = f(cdv); vv = f(vv); } + +// Ensuring that we pick the correct function for taking the address of an +// overload when conversions are involved. + +void addrof_many(int *a) __attribute__((overloadable, enable_if(0, ""))); +void addrof_many(void *a) __attribute__((overloadable)); +void addrof_many(char *a) __attribute__((overloadable)); + +void addrof_single(int *a) __attribute__((overloadable, enable_if(0, ""))); +void addrof_single(char *a) __attribute__((overloadable, enable_if(0, ""))); +void addrof_single(char *a) __attribute__((overloadable)); + +// CHECK-LABEL: define void @foo +void foo() { + // CHECK: store void (i8*)* @_Z11addrof_manyPc + void (*p1)(char *) = &addrof_many; + // CHECK: store void (i8*)* @_Z11addrof_manyPv + void (*p2)(void *) = &addrof_many; + // CHECK: void (i8*)* @_Z11addrof_manyPc + void *vp1 = (void (*)(char *)) & addrof_many; + // CHECK: void (i8*)* @_Z11addrof_manyPv + void *vp2 = (void (*)(void *)) & addrof_many; + + // CHECK: store void (i8*)* @_Z13addrof_singlePc + void (*p3)(char *) = &addrof_single; + // CHECK: @_Z13addrof_singlePc + void (*p4)(int *) = &addrof_single; + // CHECK: @_Z13addrof_singlePc + void *vp3 = &addrof_single; +} Index: test/Sema/overloadable.c =================================================================== --- test/Sema/overloadable.c +++ test/Sema/overloadable.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wincompatible-pointer-types int var __attribute__((overloadable)); // expected-error{{'overloadable' attribute only applies to functions}} void params(void) __attribute__((overloadable(12))); // expected-error {{'overloadable' attribute takes no arguments}} @@ -99,3 +99,26 @@ unsigned char *c; multi_type(c); } + +// Ensure that we allow C-specific type conversions in C +void fn_type_conversions() { + void foo(void *c) __attribute__((overloadable)); + void foo(char *c) __attribute__((overloadable)); + void (*ptr1)(void *) = &foo; + void (*ptr2)(char *) = &foo; + void (*ambiguous)(int *) = &foo; // expected-error{{initializing 'void (*)(int *)' with an expression of incompatible type ''}} expected-note@105{{candidate function}} expected-note@106{{candidate function}} + void *vp_ambiguous = &foo; // expected-error{{initializing 'void *' with an expression of incompatible type ''}} expected-note@105{{candidate function}} expected-note@106{{candidate function}} + + void (*specific1)(int *) = (void (*)(void *))&foo; // expected-warning{{incompatible pointer types initializing 'void (*)(int *)' with an expression of type 'void (*)(void *)'}} + void *specific2 = (void (*)(void *))&foo; + + void disabled(void *c) __attribute__((overloadable, enable_if(0, ""))); + void disabled(int *c) __attribute__((overloadable, enable_if(c, ""))); + void disabled(char *c) __attribute__((overloadable, enable_if(1, "The function name lies."))); + // To be clear, these should all point to the last overload of 'disabled' + void (*dptr1)(char *c) = &disabled; + void (*dptr2)(void *c) = &disabled; // expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type ''}} expected-note@115{{candidate function made ineligible by enable_if}} expected-note@116{{candidate function made ineligible by enable_if}} expected-note@117{{candidate function has type mismatch at 1st parameter (expected 'void *' but has 'char *')}} + void (*dptr3)(int *c) = &disabled; // expected-warning{{incompatible pointer types initializing 'void (*)(int *)' with an expression of type ''}} expected-note@115{{candidate function made ineligible by enable_if}} expected-note@116{{candidate function made ineligible by enable_if}} expected-note@117{{candidate function has type mismatch at 1st parameter (expected 'int *' but has 'char *')}} + + void *specific_disabled = &disabled; +} Index: test/Sema/pass-object-size.c =================================================================== --- test/Sema/pass-object-size.c +++ test/Sema/pass-object-size.c @@ -38,8 +38,8 @@ void (*p)(void *) = NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} void (*p2)(void *) = &NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} - void (*p3)(void *) = IsOverloaded; //expected-error{{initializing 'void (*)(void *)' with an expression of incompatible type ''}} expected-note@-6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-5{{type mismatch}} - void (*p4)(void *) = &IsOverloaded; //expected-error{{initializing 'void (*)(void *)' with an expression of incompatible type ''}} expected-note@-7{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-6{{type mismatch}} + void (*p3)(void *) = IsOverloaded; //expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type ''}} expected-note@-6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-5{{type mismatch}} + void (*p4)(void *) = &IsOverloaded; //expected-warning{{incompatible pointer types initializing 'void (*)(void *)' with an expression of type ''}} expected-note@-7{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-6{{type mismatch}} void (*p5)(char *) = IsOverloaded; void (*p6)(char *) = &IsOverloaded; Index: test/SemaCXX/enable_if.cpp =================================================================== --- test/SemaCXX/enable_if.cpp +++ test/SemaCXX/enable_if.cpp @@ -253,3 +253,96 @@ a = &noOvlNoCandidate; // expected-error{{cannot take address of function 'noOvlNoCandidate' becuase it has one or more non-tautological enable_if conditions}} } } + +namespace casting { +using VoidFnTy = void (*)(); + +void foo(void *c) __attribute__((enable_if(0, ""))); +void foo(int *c) __attribute__((enable_if(c, ""))); +void foo(char *c) __attribute__((enable_if(1, ""))); + +void testIt() { + auto A = reinterpret_cast(foo); + auto AAmp = reinterpret_cast(&foo); + + using VoidFooTy = void (*)(void *); + auto B = reinterpret_cast(foo); + auto BAmp = reinterpret_cast(&foo); + + using IntFooTy = void (*)(int *); + auto C = reinterpret_cast(foo); + auto CAmp = reinterpret_cast(&foo); + + using CharFooTy = void (*)(void *); + auto D = reinterpret_cast(foo); + auto DAmp = reinterpret_cast(&foo); +} + +void testItCStyle() { + auto A = (VoidFnTy)foo; + auto AAmp = (VoidFnTy)&foo; + + using VoidFooTy = void (*)(void *); + auto B = (VoidFooTy)foo; + auto BAmp = (VoidFooTy)&foo; + + using IntFooTy = void (*)(int *); + auto C = (IntFooTy)foo; + auto CAmp = (IntFooTy)&foo; + + using CharFooTy = void (*)(void *); + auto D = (CharFooTy)foo; + auto DAmp = (CharFooTy)&foo; +} +} + +namespace casting_templates { +template void foo(T) {} // expected-note 4 {{candidate function}} + +void foo(int *c) __attribute__((enable_if(c, ""))); //expected-note 4 {{candidate function}} +void foo(char *c) __attribute__((enable_if(c, ""))); //expected-note 4 {{candidate function}} + +void testIt() { + using IntFooTy = void (*)(int *); + auto A = reinterpret_cast(foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto ARef = reinterpret_cast(&foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto AExplicit = reinterpret_cast(foo); + + using CharFooTy = void (*)(char *); + auto B = reinterpret_cast(foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto BRef = reinterpret_cast(&foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto BExplicit = reinterpret_cast(foo); +} + +void testItCStyle() { + // constexpr is usable here because all of these should become static_casts. + using IntFooTy = void (*)(int *); + constexpr auto A = (IntFooTy)foo; + constexpr auto ARef = (IntFooTy)&foo; + constexpr auto AExplicit = (IntFooTy)foo; + + using CharFooTy = void (*)(char *); + constexpr auto B = (CharFooTy)foo; + constexpr auto BRef = (CharFooTy)&foo; + constexpr auto BExplicit = (CharFooTy)foo; + + static_assert(A == ARef && ARef == AExplicit, ""); + static_assert(B == BRef && BRef == BExplicit, ""); +} +} + +namespace multiple_matches { +using NoMatchTy = void (*)(); + +void foo(float *c); //expected-note 4 {{candidate function}} +void foo(int *c) __attribute__((enable_if(1, ""))); //expected-note 4 {{candidate function}} +void foo(char *c) __attribute__((enable_if(1, ""))); //expected-note 4 {{candidate function}} + +void testIt() { + auto A = reinterpret_cast(foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto ARef = reinterpret_cast(&foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + + auto C = (NoMatchTy)foo; // expected-error{{address of overloaded function 'foo' does not match required type 'void ()'}} + auto CRef = (NoMatchTy)&foo; // expected-error{{address of overloaded function 'foo' does not match required type 'void ()'}} +} +}