diff options
| author | George Burgess IV <george.burgess.iv@gmail.com> | 2016-06-08 00:34:22 +0000 |
|---|---|---|
| committer | George Burgess IV <george.burgess.iv@gmail.com> | 2016-06-08 00:34:22 +0000 |
| commit | beca4a3338ab39dcdc81924734e95863fb53e5bb (patch) | |
| tree | 93431375f9b3618f0e141d8b4c74fd86bb507b9b | |
| parent | 140065a693f5fdcc52f7aa7fef58815b746f0317 (diff) | |
| download | bcm5719-llvm-beca4a3338ab39dcdc81924734e95863fb53e5bb.tar.gz bcm5719-llvm-beca4a3338ab39dcdc81924734e95863fb53e5bb.zip | |
[Sema] Teach CheckPlaceholderExpr about unaddressable functions.
Given the following C++:
```
void foo();
void foo() __attribute__((enable_if(false, "")));
bool bar() {
auto P = foo;
return P == foo;
}
```
We'll currently happily (and correctly) resolve `foo` to the `foo`
overload without `enable_if` when assigning to `P`. However, we'll
complain about an ambiguous overload on the `P == foo` line, because
`Sema::CheckPlaceholderExpr` doesn't recognize that there's only one
`foo` that could possibly work here.
This patch teaches `Sema::CheckPlaceholderExpr` how to properly deal
with such cases.
Grepping for other callers of things like
`Sema::ResolveAndFixSingleFunctionTemplateSpecialization`, it *looks*
like this is the last place that needed to be fixed up. If I'm wrong,
I'll see if there's something we can do that beats what amounts to
whack-a-mole with bugs.
llvm-svn: 272080
| -rw-r--r-- | clang/include/clang/Sema/Sema.h | 2 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaCast.cpp | 22 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 20 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 30 | ||||
| -rw-r--r-- | clang/test/SemaCXX/unaddressable-functions.cpp | 45 |
5 files changed, 94 insertions, 25 deletions
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2e068cff890..62f37fda52d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2567,6 +2567,8 @@ public: resolveAddressOfOnlyViableOverloadCandidate(Expr *E, DeclAccessPair &FoundResult); + bool resolveAndFixAddressOfOnlyViableOverloadCandidate(ExprResult &SrcExpr); + FunctionDecl * ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, bool Complain = false, diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp index 7239d44ca23..df32dbdb150 100644 --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -1862,24 +1862,12 @@ static bool fixOverloadedReinterpretCastExpr(Sema &Self, QualType DestType, Result.isUsable()) return true; - DeclAccessPair DAP; - FunctionDecl *Found = Self.resolveAddressOfOnlyViableOverloadCandidate(E, DAP); - if (!Found) + // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization + // preserves Result. + Result = E; + if (!Self.resolveAndFixAddressOfOnlyViableOverloadCandidate(Result)) 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(); + return Result.isUsable(); } static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 29be09e8fea..f4a5dea8f94 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14911,16 +14911,20 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) { case BuiltinType::Overload: { // Try to resolve a single function template specialization. // This is obligatory. - ExprResult result = E; - if (ResolveAndFixSingleFunctionTemplateSpecialization(result, false)) { - return result; + ExprResult Result = E; + if (ResolveAndFixSingleFunctionTemplateSpecialization(Result, false)) + return Result; + + // No guarantees that ResolveAndFixSingleFunctionTemplateSpecialization + // leaves Result unchanged on failure. + Result = E; + if (resolveAndFixAddressOfOnlyViableOverloadCandidate(Result)) + return Result; // If that failed, try to recover with a call. - } else { - tryToRecoverWithCall(result, PDiag(diag::err_ovl_unresolvable), - /*complain*/ true); - return result; - } + tryToRecoverWithCall(Result, PDiag(diag::err_ovl_unresolvable), + /*complain*/ true); + return Result; } // Bound member functions. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 1c41487f5a4..baf673b5e68 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10729,6 +10729,36 @@ Sema::resolveAddressOfOnlyViableOverloadCandidate(Expr *E, return Result; } +/// \brief Given an overloaded function, tries to turn it into a non-overloaded +/// function reference using resolveAddressOfOnlyViableOverloadCandidate. This +/// will perform access checks, diagnose the use of the resultant decl, and, if +/// necessary, perform a function-to-pointer decay. +/// +/// Returns false if resolveAddressOfOnlyViableOverloadCandidate fails. +/// Otherwise, returns true. This may emit diagnostics and return true. +bool Sema::resolveAndFixAddressOfOnlyViableOverloadCandidate( + ExprResult &SrcExpr) { + Expr *E = SrcExpr.get(); + assert(E->getType() == Context.OverloadTy && "SrcExpr must be an overload"); + + DeclAccessPair DAP; + FunctionDecl *Found = resolveAddressOfOnlyViableOverloadCandidate(E, DAP); + if (!Found) + return false; + + // Emitting multiple diagnostics for a function that is both inaccessible and + // unavailable is consistent with our behavior elsewhere. So, always check + // for both. + DiagnoseUseOfDecl(Found, E->getExprLoc()); + CheckAddressOfMemberAccess(E, DAP); + Expr *Fixed = FixOverloadedFunctionReference(E, DAP, Found); + if (Fixed->getType()->isFunctionType()) + SrcExpr = DefaultFunctionArrayConversion(Fixed, /*Diagnose=*/false); + else + SrcExpr = Fixed; + return true; +} + /// \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/clang/test/SemaCXX/unaddressable-functions.cpp b/clang/test/SemaCXX/unaddressable-functions.cpp index 823f1be05f8..286cbee5ea9 100644 --- a/clang/test/SemaCXX/unaddressable-functions.cpp +++ b/clang/test/SemaCXX/unaddressable-functions.cpp @@ -100,3 +100,48 @@ auto Fail = call(&foo); // expected-error{{no matching function for call to 'cal auto PtrOk = &foo<int>; auto PtrFail = &foo; // expected-error{{variable 'PtrFail' with type 'auto' has incompatible initializer of type '<overloaded function type>'}} } + +namespace pointer_equality { + using FnTy = void (*)(); + + void bothEnableIf() __attribute__((enable_if(false, ""))); + void bothEnableIf() __attribute__((enable_if(true, ""))); + + void oneEnableIf() __attribute__((enable_if(false, ""))); + void oneEnableIf(); + + void test() { + FnTy Fn; + (void)(Fn == bothEnableIf); + (void)(Fn == &bothEnableIf); + (void)(Fn == oneEnableIf); + (void)(Fn == &oneEnableIf); + } + + void unavailableEnableIf() __attribute__((enable_if(false, ""))); + void unavailableEnableIf() __attribute__((unavailable("noooo"))); // expected-note 2{{marked unavailable here}} + + void testUnavailable() { + FnTy Fn; + (void)(Fn == unavailableEnableIf); // expected-error{{is unavailable}} + (void)(Fn == &unavailableEnableIf); // expected-error{{is unavailable}} + } + + class Foo { + static void staticAccessEnableIf(); // expected-note 2{{declared private here}} + void accessEnableIf(); // expected-note{{declared private here}} + + public: + static void staticAccessEnableIf() __attribute__((enable_if(false, ""))); + void accessEnableIf() __attribute__((enable_if(false, ""))); + }; + + void testAccess() { + FnTy Fn; + (void)(Fn == Foo::staticAccessEnableIf); // expected-error{{is a private member}} + (void)(Fn == &Foo::staticAccessEnableIf); // expected-error{{is a private member}} + + void (Foo::*MemFn)(); + (void)(MemFn == &Foo::accessEnableIf); // expected-error{{is a private member}} + } +} |

