mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-15 23:40:54 +00:00
[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
This commit is contained in:
parent
140065a693
commit
beca4a3338
@ -2567,6 +2567,8 @@ public:
|
||||
resolveAddressOfOnlyViableOverloadCandidate(Expr *E,
|
||||
DeclAccessPair &FoundResult);
|
||||
|
||||
bool resolveAndFixAddressOfOnlyViableOverloadCandidate(ExprResult &SrcExpr);
|
||||
|
||||
FunctionDecl *
|
||||
ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl,
|
||||
bool Complain = false,
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user