]> granicus.if.org Git - clang/commitdiff
[Sema] Teach CheckPlaceholderExpr about unaddressable functions.
authorGeorge Burgess IV <george.burgess.iv@gmail.com>
Wed, 8 Jun 2016 00:34:22 +0000 (00:34 +0000)
committerGeorge Burgess IV <george.burgess.iv@gmail.com>
Wed, 8 Jun 2016 00:34:22 +0000 (00:34 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@272080 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Sema.h
lib/Sema/SemaCast.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/unaddressable-functions.cpp

index 2e068cff89000b418f9a22ed3927b6fad87b32f5..62f37fda52d35b029d21100cf557e219b1cb1200 100644 (file)
@@ -2567,6 +2567,8 @@ public:
   resolveAddressOfOnlyViableOverloadCandidate(Expr *E,
                                               DeclAccessPair &FoundResult);
 
+  bool resolveAndFixAddressOfOnlyViableOverloadCandidate(ExprResult &SrcExpr);
+
   FunctionDecl *
   ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl,
                                               bool Complain = false,
index 7239d44ca232bf29ab15b5f574c2ef775c69d90e..df32dbdb1501421a10278d1da10c9db98a163689 100644 (file)
@@ -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,
index 29be09e8feaedadd6734166ee26d569a3b42ff99..f4a5dea8f94c991b6db0d0d5a3712f9e3bee34b7 100644 (file)
@@ -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.
index 1c41487f5a4c09fc6c72ef00e7c86e088fa78c97..baf673b5e682cf239116299c8a02d2d3aa2468c7 100644 (file)
@@ -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.
 ///
index 823f1be05f80c69e65066c3e57e7c83da1439e6a..286cbee5ea9bc8f26003674d6a094da69137da20 100644 (file)
@@ -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}}
+  }
+}