From e754ecb43be2be2ef7417f13d6ee98c54ee5f5fd Mon Sep 17 00:00:00 2001 From: George Burgess IV <george.burgess.iv@gmail.com> Date: Sat, 19 Mar 2016 21:36:10 +0000 Subject: [PATCH] [Sema] Allow casting of some overloaded functions Some functions can't have their address taken. If we encounter an overload set where only one of the candidates can have its address taken, we should automatically select that candidate in cast expressions. Differential Revision: http://reviews.llvm.org/D17701 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263887 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 4 + lib/Sema/SemaCast.cpp | 58 +++++++++++---- lib/Sema/SemaOverload.cpp | 38 +++++++++- test/SemaCXX/enable_if.cpp | 93 ++++++++++++++++++++++++ test/SemaCXX/unaddressable-functions.cpp | 36 +++++++++ 5 files changed, 215 insertions(+), 14 deletions(-) create mode 100644 test/SemaCXX/unaddressable-functions.cpp diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 27c35e944f1..d16293d78e0 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2506,6 +2506,10 @@ public: DeclAccessPair &Found, bool *pHadMultipleCandidates = nullptr); + FunctionDecl * + resolveAddressOfOnlyViableOverloadCandidate(Expr *E, + DeclAccessPair &FoundResult); + FunctionDecl * ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, bool Complain = false, diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index ad1d7da4d07..7207d04158a 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -1750,6 +1750,44 @@ static void checkIntToPointerCast(bool CStyle, SourceLocation Loc, } } +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. + + Expr *E = Result.get(); + // 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; + + DeclAccessPair DAP; + FunctionDecl *Found = Self.resolveAddressOfOnlyViableOverloadCandidate(E, DAP); + if (!Found) + return false; + + // It seems that if we encounter a call to a function that is both unavailable + // and inaccessible, we'll emit multiple diags for said call. Hence, we run + // both checks below unconditionally. + Self.DiagnoseUseOfDecl(Found, E->getExprLoc()); + Self.CheckAddressOfMemberAccess(E, DAP); + + Expr *Fixed = Self.FixOverloadedFunctionReference(E, DAP, Found); + if (Fixed->getType()->isFunctionType()) + Result = Self.DefaultFunctionArrayConversion(Fixed, /*Diagnose=*/false); + else + Result = Fixed; + + return !Result.isInvalid(); +} + static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, bool CStyle, SourceRange OpRange, @@ -1761,21 +1799,15 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, 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<int> 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"); + SrcExpr = FixedExpr; + SrcType = SrcExpr.get()->getType(); } if (const ReferenceType *DestTypeTmp = DestType->getAs<ReferenceType>()) { diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index a28ded474a7..3bb9a26c438 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -10419,7 +10419,7 @@ private: return false; QualType ResultTy; - if (Context.hasSameUnqualifiedType(TargetFunctionType, + if (Context.hasSameUnqualifiedType(TargetFunctionType, FunDecl->getType()) || S.IsNoReturnConversion(FunDecl->getType(), TargetFunctionType, ResultTy) || @@ -10651,6 +10651,42 @@ Sema::ResolveAddressOfOverloadedFunction(Expr *AddressOfExpr, return Fn; } +/// \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 will modify `Pair` iff it returns non-null. +/// +/// This routine can only realistically succeed if all but one candidates in the +/// overload set for SrcExpr cannot have their addresses taken. +FunctionDecl * +Sema::resolveAddressOfOnlyViableOverloadCandidate(Expr *E, + DeclAccessPair &Pair) { + OverloadExpr::FindResult R = OverloadExpr::find(E); + OverloadExpr *Ovl = R.Expression; + FunctionDecl *Result = nullptr; + DeclAccessPair DAP; + // Don't use the AddressOfResolver because we're specifically looking for + // cases where we have one overload candidate that lacks + // enable_if/pass_object_size/... + for (auto I = Ovl->decls_begin(), E = Ovl->decls_end(); I != E; ++I) { + auto *FD = dyn_cast<FunctionDecl>(I->getUnderlyingDecl()); + if (!FD) + return nullptr; + + if (!checkAddressOfFunctionIsAvailable(FD)) + continue; + + // We have more than one result; quit. + if (Result) + return nullptr; + DAP = I.getPair(); + Result = FD; + } + + if (Result) + Pair = DAP; + return Result; +} + /// \brief Given an expression that refers to an overloaded function, try to /// resolve that overloaded function expression down to a single function. /// diff --git a/test/SemaCXX/enable_if.cpp b/test/SemaCXX/enable_if.cpp index cd8241808c9..14152d30897 100644 --- a/test/SemaCXX/enable_if.cpp +++ b/test/SemaCXX/enable_if.cpp @@ -253,3 +253,96 @@ namespace FnPtrs { 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<VoidFnTy>(foo); + auto AAmp = reinterpret_cast<VoidFnTy>(&foo); + + using VoidFooTy = void (*)(void *); + auto B = reinterpret_cast<VoidFooTy>(foo); + auto BAmp = reinterpret_cast<VoidFooTy>(&foo); + + using IntFooTy = void (*)(int *); + auto C = reinterpret_cast<IntFooTy>(foo); + auto CAmp = reinterpret_cast<IntFooTy>(&foo); + + using CharFooTy = void (*)(void *); + auto D = reinterpret_cast<CharFooTy>(foo); + auto DAmp = reinterpret_cast<CharFooTy>(&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 <typename T> 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<IntFooTy>(foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto ARef = reinterpret_cast<IntFooTy>(&foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto AExplicit = reinterpret_cast<IntFooTy>(foo<int*>); + + using CharFooTy = void (*)(char *); + auto B = reinterpret_cast<CharFooTy>(foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto BRef = reinterpret_cast<CharFooTy>(&foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto BExplicit = reinterpret_cast<CharFooTy>(foo<char*>); +} + +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<int*>; + + using CharFooTy = void (*)(char *); + constexpr auto B = (CharFooTy)foo; + constexpr auto BRef = (CharFooTy)&foo; + constexpr auto BExplicit = (CharFooTy)foo<char*>; + + 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<NoMatchTy>(foo); // expected-error{{reinterpret_cast cannot resolve overloaded function 'foo' to type}} + auto ARef = reinterpret_cast<NoMatchTy>(&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 ()'}} +} +} diff --git a/test/SemaCXX/unaddressable-functions.cpp b/test/SemaCXX/unaddressable-functions.cpp new file mode 100644 index 00000000000..a382ccfa6cd --- /dev/null +++ b/test/SemaCXX/unaddressable-functions.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++14 + +namespace access_control { +class Private { + void check(int *) __attribute__((enable_if(false, ""))); + void check(double *) __attribute__((enable_if(true, ""))); + + static void checkStatic(int *) __attribute__((enable_if(false, ""))); + static void checkStatic(double *) __attribute__((enable_if(true, ""))); +}; + +auto Priv = reinterpret_cast<void (Private::*)(char *)>(&Private::check); // expected-error{{'check' is a private member of 'access_control::Private'}} expected-note@6{{implicitly declared private here}} + +auto PrivStatic = reinterpret_cast<void (*)(char *)>(&Private::checkStatic); // expected-error{{'checkStatic' is a private member of 'access_control::Private'}} expected-note@9{{implicitly declared private here}} + +class Protected { +protected: + void check(int *) __attribute__((enable_if(false, ""))); + void check(double *) __attribute__((enable_if(true, ""))); + + static void checkStatic(int *) __attribute__((enable_if(false, ""))); + static void checkStatic(double *) __attribute__((enable_if(true, ""))); +}; + +auto Prot = reinterpret_cast<void (Protected::*)(char *)>(&Protected::check); // expected-error{{'check' is a protected member of 'access_control::Protected'}} expected-note@19{{declared protected here}} + +auto ProtStatic = reinterpret_cast<void (*)(char *)>(&Protected::checkStatic); // expected-error{{'checkStatic' is a protected member of 'access_control::Protected'}} expected-note@22{{declared protected here}} +} + +namespace unavailable { +// Ensure that we check that the function can be called +void foo() __attribute__((unavailable("don't call this"))); +void foo(int) __attribute__((enable_if(false, ""))); + +void *Ptr = reinterpret_cast<void*>(foo); // expected-error{{'foo' is unavailable: don't call this}} expected-note@-3{{explicitly marked unavailable here}} +} -- GitLab