]> granicus.if.org Git - clang/commitdiff
[Sema] Allow casting of some overloaded functions
authorGeorge Burgess IV <george.burgess.iv@gmail.com>
Sat, 19 Mar 2016 21:36:10 +0000 (21:36 +0000)
committerGeorge Burgess IV <george.burgess.iv@gmail.com>
Sat, 19 Mar 2016 21:36:10 +0000 (21:36 +0000)
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
lib/Sema/SemaCast.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/enable_if.cpp
test/SemaCXX/unaddressable-functions.cpp [new file with mode: 0644]

index 27c35e944f18d6b93f3101df40d9212f2342ac8f..d16293d78e082ccdedaf665d154d488cb2c6c613 100644 (file)
@@ -2506,6 +2506,10 @@ public:
                                      DeclAccessPair &Found,
                                      bool *pHadMultipleCandidates = nullptr);
 
+  FunctionDecl *
+  resolveAddressOfOnlyViableOverloadCandidate(Expr *E,
+                                              DeclAccessPair &FoundResult);
+
   FunctionDecl *
   ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl,
                                               bool Complain = false,
index ad1d7da4d070cd00c29ca7901eb38fff34256cc6..7207d04158a9854c50891d94fd340e71c7c22c05 100644 (file)
@@ -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>()) {
index a28ded474a75fd431d5bed5a8047a741675668e3..3bb9a26c438f6ceec8f78695ab7801f75b8232fb 100644 (file)
@@ -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.
 ///
index cd8241808c94623728e6ed12b98c64769d641120..14152d308970ff6d425c17394d2ea072ce919230 100644 (file)
@@ -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 (file)
index 0000000..a382ccf
--- /dev/null
@@ -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}}
+}