Skip to content

Commit

Permalink
[Sema] Allow casting of some overloaded functions
Browse files Browse the repository at this point in the history
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

llvm-svn: 263887
  • Loading branch information
gburgessiv committed Mar 19, 2016
1 parent 43165d9 commit 3cde9bf
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 14 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2506,6 +2506,10 @@ class Sema {
DeclAccessPair &Found,
bool *pHadMultipleCandidates = nullptr);

FunctionDecl *
resolveAddressOfOnlyViableOverloadCandidate(Expr *E,
DeclAccessPair &FoundResult);

FunctionDecl *
ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl,
bool Complain = false,
Expand Down
58 changes: 45 additions & 13 deletions clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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>()) {
Expand Down
38 changes: 37 additions & 1 deletion clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10419,7 +10419,7 @@ class AddressOfFunctionResolver {
return false;

QualType ResultTy;
if (Context.hasSameUnqualifiedType(TargetFunctionType,
if (Context.hasSameUnqualifiedType(TargetFunctionType,
FunDecl->getType()) ||
S.IsNoReturnConversion(FunDecl->getType(), TargetFunctionType,
ResultTy) ||
Expand Down Expand Up @@ -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.
///
Expand Down
93 changes: 93 additions & 0 deletions clang/test/SemaCXX/enable_if.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()'}}
}
}
36 changes: 36 additions & 0 deletions clang/test/SemaCXX/unaddressable-functions.cpp
Original file line number Diff line number Diff line change
@@ -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}}
}

0 comments on commit 3cde9bf

Please sign in to comment.