Fix handling of default arguments in __attribute__((enable_if)).

We didn't properly build default argument expressions previously -- we
failed to build the wrapper CXXDefaultArgExpr node, which meant that
std::source_location misbehaved, and we didn't perform default argument
instantiation when necessary, which meant that dependent default
arguments in function templates didn't work at all.
This commit is contained in:
Richard Smith 2020-05-28 15:02:18 -07:00
parent b0b2507717
commit 0dfb43deb6
4 changed files with 35 additions and 25 deletions

View File

@ -3371,7 +3371,8 @@ public:
/// Check the enable_if expressions on the given function. Returns the first
/// failing attribute, or NULL if they were all successful.
EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
EnableIfAttr *CheckEnableIf(FunctionDecl *Function, SourceLocation CallLoc,
ArrayRef<Expr *> Args,
bool MissingImplicitThis = false);
/// Find the failed Boolean condition within a given Boolean

View File

@ -6060,7 +6060,8 @@ static void checkDirectCallValidity(Sema &S, const Expr *Fn,
if (Callee->getMinRequiredArguments() > ArgExprs.size())
return;
if (const EnableIfAttr *Attr = S.CheckEnableIf(Callee, ArgExprs, true)) {
if (const EnableIfAttr *Attr =
S.CheckEnableIf(Callee, Fn->getBeginLoc(), ArgExprs, true)) {
S.Diag(Fn->getBeginLoc(),
isa<CXXMethodDecl>(Callee)
? diag::err_ovl_no_viable_member_function_in_call

View File

@ -6356,7 +6356,8 @@ void Sema::AddOverloadCandidate(
}
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Function, Args)) {
if (EnableIfAttr *FailedAttr =
CheckEnableIf(Function, CandidateSet.getLocation(), Args)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
@ -6462,11 +6463,10 @@ Sema::SelectBestMethod(Selector Sel, MultiExprArg Args, bool IsInstance,
return nullptr;
}
static bool
convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg,
ArrayRef<Expr *> Args, Sema::SFINAETrap &Trap,
bool MissingImplicitThis, Expr *&ConvertedThis,
SmallVectorImpl<Expr *> &ConvertedArgs) {
static bool convertArgsForAvailabilityChecks(
Sema &S, FunctionDecl *Function, Expr *ThisArg, SourceLocation CallLoc,
ArrayRef<Expr *> Args, Sema::SFINAETrap &Trap, bool MissingImplicitThis,
Expr *&ConvertedThis, SmallVectorImpl<Expr *> &ConvertedArgs) {
if (ThisArg) {
CXXMethodDecl *Method = cast<CXXMethodDecl>(Function);
assert(!isa<CXXConstructorDecl>(Method) &&
@ -6511,17 +6511,7 @@ convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg,
if (!Function->isVariadic() && Args.size() < Function->getNumParams()) {
for (unsigned i = Args.size(), e = Function->getNumParams(); i != e; ++i) {
ParmVarDecl *P = Function->getParamDecl(i);
Expr *DefArg = P->hasUninstantiatedDefaultArg()
? P->getUninstantiatedDefaultArg()
: P->getDefaultArg();
// This can only happen in code completion, i.e. when PartialOverloading
// is true.
if (!DefArg)
return false;
ExprResult R =
S.PerformCopyInitialization(InitializedEntity::InitializeParameter(
S.Context, Function->getParamDecl(i)),
SourceLocation(), DefArg);
ExprResult R = S.BuildCXXDefaultArgExpr(CallLoc, Function, P);
if (R.isInvalid())
return false;
ConvertedArgs.push_back(R.get());
@ -6533,7 +6523,9 @@ convertArgsForAvailabilityChecks(Sema &S, FunctionDecl *Function, Expr *ThisArg,
return true;
}
EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function,
SourceLocation CallLoc,
ArrayRef<Expr *> Args,
bool MissingImplicitThis) {
auto EnableIfAttrs = Function->specific_attrs<EnableIfAttr>();
if (EnableIfAttrs.begin() == EnableIfAttrs.end())
@ -6544,7 +6536,7 @@ EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
// FIXME: We should look into making enable_if late-parsed.
Expr *DiscardedThis;
if (!convertArgsForAvailabilityChecks(
*this, Function, /*ThisArg=*/nullptr, Args, Trap,
*this, Function, /*ThisArg=*/nullptr, CallLoc, Args, Trap,
/*MissingImplicitThis=*/true, DiscardedThis, ConvertedArgs))
return *EnableIfAttrs.begin();
@ -6874,7 +6866,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
}
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Method, Args, true)) {
if (EnableIfAttr *FailedAttr =
CheckEnableIf(Method, CandidateSet.getLocation(), Args, true)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
@ -7327,7 +7320,8 @@ void Sema::AddConversionCandidate(
"Can only end up with a standard conversion sequence or failure");
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) {
if (EnableIfAttr *FailedAttr =
CheckEnableIf(Conversion, CandidateSet.getLocation(), None)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
@ -7497,7 +7491,8 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion,
}
}
if (EnableIfAttr *FailedAttr = CheckEnableIf(Conversion, None)) {
if (EnableIfAttr *FailedAttr =
CheckEnableIf(Conversion, CandidateSet.getLocation(), None)) {
Candidate.Viable = false;
Candidate.FailureKind = ovl_fail_enable_if;
Candidate.DeductionFailure.Data = FailedAttr;
@ -14130,7 +14125,8 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
// resolution process, we still need to handle the enable_if attribute. Do
// that here, so it will not hide previous -- and more relevant -- errors.
if (auto *MemE = dyn_cast<MemberExpr>(NakedMemExpr)) {
if (const EnableIfAttr *Attr = CheckEnableIf(Method, Args, true)) {
if (const EnableIfAttr *Attr =
CheckEnableIf(Method, LParenLoc, Args, true)) {
Diag(MemE->getMemberLoc(),
diag::err_ovl_no_viable_member_function_in_call)
<< Method << Method->getSourceRange();

View File

@ -561,3 +561,15 @@ namespace IgnoreUnusedArgSideEffects {
float &x = h();
#endif
}
namespace DefaultArgs {
void f(int n = __builtin_LINE()) __attribute__((enable_if(n == 12345, "only callable on line 12345"))); // expected-note {{only callable on line 12345}}
void g() { f(); } // expected-error {{no matching function}}
#line 12345
void h() { f(); }
template<typename T> void x(int n = T()) __attribute__((enable_if(n == 0, ""))) {} // expected-note {{candidate}}
void y() { x<int>(); }
struct Z { constexpr operator int() const { return 1; } };
void z() { x<Z>(); } // expected-error {{no matching function}}
}