diff options
Diffstat (limited to 'clang/lib/Sema/SemaTemplate.cpp')
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 138 |
1 files changed, 131 insertions, 7 deletions
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index a8923ce9e27..58614322f76 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2806,6 +2806,67 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, llvm_unreachable("unexpected BuiltinTemplateDecl!"); } +/// Determine whether this alias template is "enable_if_t". +static bool isEnableIfAliasTemplate(TypeAliasTemplateDecl *AliasTemplate) { + return AliasTemplate->getName().equals("enable_if_t"); +} + +/// Collect all of the separable terms in the given condition, which +/// might be a conjunction. +/// +/// FIXME: The right answer is to convert the logical expression into +/// disjunctive normal form, so we can find the first failed term +/// within each possible clause. +static void collectConjunctionTerms(Expr *Clause, + SmallVectorImpl<Expr *> &Terms) { + if (auto BinOp = dyn_cast<BinaryOperator>(Clause->IgnoreParenImpCasts())) { + if (BinOp->getOpcode() == BO_LAnd) { + collectConjunctionTerms(BinOp->getLHS(), Terms); + collectConjunctionTerms(BinOp->getRHS(), Terms); + } + + return; + } + + Terms.push_back(Clause); +} + +/// Find the failed subexpression within enable_if, and describe it +/// with a string. +static std::pair<Expr *, std::string> +findFailedEnableIfCondition(Sema &S, Expr *Cond) { + // Separate out all of the terms in a conjunction. + SmallVector<Expr *, 4> Terms; + collectConjunctionTerms(Cond, Terms); + + // Determine which term failed. + Expr *FailedCond = nullptr; + for (Expr *Term : Terms) { + // The initialization of the parameter from the argument is + // a constant-evaluated context. + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + bool Succeeded; + if (Term->EvaluateAsBooleanCondition(Succeeded, S.Context) && + !Succeeded) { + FailedCond = Term->IgnoreParenImpCasts(); + break; + } + } + + if (!FailedCond) + FailedCond = Cond->IgnoreParenImpCasts(); + + std::string Description; + { + llvm::raw_string_ostream Out(Description); + FailedCond->printPretty(Out, nullptr, + PrintingPolicy(S.Context.getLangOpts())); + } + return { FailedCond, Description }; +} + QualType Sema::CheckTemplateIdType(TemplateName Name, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs) { @@ -2852,12 +2913,12 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, if (Pattern->isInvalidDecl()) return QualType(); - TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, - Converted); + TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack, + Converted); // Only substitute for the innermost template argument list. MultiLevelTemplateArgumentList TemplateArgLists; - TemplateArgLists.addOuterTemplateArguments(&TemplateArgs); + TemplateArgLists.addOuterTemplateArguments(&StackTemplateArgs); unsigned Depth = AliasTemplate->getTemplateParameters()->getDepth(); for (unsigned I = 0; I < Depth; ++I) TemplateArgLists.addOuterTemplateArguments(None); @@ -2870,8 +2931,42 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, CanonType = SubstType(Pattern->getUnderlyingType(), TemplateArgLists, AliasTemplate->getLocation(), AliasTemplate->getDeclName()); - if (CanonType.isNull()) + if (CanonType.isNull()) { + // If this was enable_if and we failed to find the nested type + // within enable_if in a SFINAE context, dig out the specific + // enable_if condition that failed and present that instead. + if (isEnableIfAliasTemplate(AliasTemplate)) { + if (auto DeductionInfo = isSFINAEContext()) { + if (*DeductionInfo && + (*DeductionInfo)->hasSFINAEDiagnostic() && + (*DeductionInfo)->peekSFINAEDiagnostic().second.getDiagID() == + diag::err_typename_nested_not_found_enable_if && + TemplateArgs[0].getArgument().getKind() + == TemplateArgument::Expression) { + Expr *FailedCond; + std::string FailedDescription; + std::tie(FailedCond, FailedDescription) = + findFailedEnableIfCondition( + *this, TemplateArgs[0].getSourceExpression()); + + // Remove the old SFINAE diagnostic. + PartialDiagnosticAt OldDiag = + {SourceLocation(), PartialDiagnostic::NullDiagnostic()}; + (*DeductionInfo)->takeSFINAEDiagnostic(OldDiag); + + // Add a new SFINAE diagnostic specifying which condition + // failed. + (*DeductionInfo)->addSFINAEDiagnostic( + OldDiag.first, + PDiag(diag::err_typename_nested_not_found_requirement) + << FailedDescription + << FailedCond->getSourceRange()); + } + } + } + return QualType(); + } } else if (Name.isDependent() || TemplateSpecializationType::anyDependentTemplateArguments( TemplateArgs, InstantiationDependent)) { @@ -9290,7 +9385,7 @@ Sema::ActOnTypenameType(Scope *S, /// Determine whether this failed name lookup should be treated as being /// disabled by a usage of std::enable_if. static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, - SourceRange &CondRange) { + SourceRange &CondRange, Expr *&Cond) { // We must be looking for a ::type... if (!II.isStr("type")) return false; @@ -9320,6 +9415,19 @@ static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, // Assume the first template argument is the condition. CondRange = EnableIfTSTLoc.getArgLoc(0).getSourceRange(); + + // Dig out the condition. + Cond = nullptr; + if (EnableIfTSTLoc.getArgLoc(0).getArgument().getKind() + != TemplateArgument::Expression) + return true; + + Cond = EnableIfTSTLoc.getArgLoc(0).getSourceExpression(); + + // Ignore Boolean literals; they add no value. + if (isa<CXXBoolLiteralExpr>(Cond->IgnoreParenCasts())) + Cond = nullptr; + return true; } @@ -9363,9 +9471,25 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // If we're looking up 'type' within a template named 'enable_if', produce // a more specific diagnostic. SourceRange CondRange; - if (isEnableIf(QualifierLoc, II, CondRange)) { + Expr *Cond = nullptr; + if (isEnableIf(QualifierLoc, II, CondRange, Cond)) { + // If we have a condition, narrow it down to the specific failed + // condition. + if (Cond) { + Expr *FailedCond; + std::string FailedDescription; + std::tie(FailedCond, FailedDescription) = + findFailedEnableIfCondition(*this, Cond); + + Diag(FailedCond->getExprLoc(), + diag::err_typename_nested_not_found_requirement) + << FailedDescription + << FailedCond->getSourceRange(); + return QualType(); + } + Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if) - << Ctx << CondRange; + << Ctx << CondRange; return QualType(); } |