diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c521b56a98606..6756b0d924be6 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -385,6 +385,7 @@ Bug Fixes to C++ Support - Improved fix for an issue with pack expansions of type constraints, where this now also works if the constraint has non-type or template template parameters. (#GH131798) +- Fixes to partial ordering of non-type template parameter packs. (#GH132562) - Fix crash when evaluating the trailing requires clause of generic lambdas which are part of a pack expansion. - Fixes matching of nested template template parameters. (#GH130362) diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index ac78d2faefe42..c613ce162a6a4 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4209,10 +4209,10 @@ class PackExpansionExpr : public Expr { Stmt *Pattern; public: - PackExpansionExpr(QualType T, Expr *Pattern, SourceLocation EllipsisLoc, + PackExpansionExpr(Expr *Pattern, SourceLocation EllipsisLoc, UnsignedOrNone NumExpansions) - : Expr(PackExpansionExprClass, T, Pattern->getValueKind(), - Pattern->getObjectKind()), + : Expr(PackExpansionExprClass, Pattern->getType(), + Pattern->getValueKind(), Pattern->getObjectKind()), EllipsisLoc(EllipsisLoc), NumExpansions(NumExpansions ? *NumExpansions + 1 : 0), Pattern(Pattern) { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b835697f99670..0f4377540bb51 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10126,13 +10126,14 @@ class Sema final : public SemaBase { /// Contexts in which a converted constant expression is required. enum CCEKind { - CCEK_CaseValue, ///< Expression in a case label. - CCEK_Enumerator, ///< Enumerator value with fixed underlying type. - CCEK_TemplateArg, ///< Value of a non-type template parameter. - CCEK_InjectedTTP, ///< Injected parameter of a template template parameter. - CCEK_ArrayBound, ///< Array bound in array declarator or new-expression. - CCEK_ExplicitBool, ///< Condition in an explicit(bool) specifier. - CCEK_Noexcept, ///< Condition in a noexcept(bool) specifier. + CCEK_CaseValue, ///< Expression in a case label. + CCEK_Enumerator, ///< Enumerator value with fixed underlying type. + CCEK_TemplateArg, ///< Value of a non-type template parameter. + CCEK_TempArgStrict, ///< As above, but applies strict template checking + ///< rules. + CCEK_ArrayBound, ///< Array bound in array declarator or new-expression. + CCEK_ExplicitBool, ///< Condition in an explicit(bool) specifier. + CCEK_Noexcept, ///< Condition in a noexcept(bool) specifier. CCEK_StaticAssertMessageSize, ///< Call to size() in a static assert ///< message. CCEK_StaticAssertMessageData, ///< Call to data() in a static assert @@ -11894,7 +11895,7 @@ class Sema final : public SemaBase { QualType InstantiatedParamType, Expr *Arg, TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted, - bool MatchingTTP, + bool StrictCheck, CheckTemplateArgumentKind CTAK); /// Check a template argument against its corresponding diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 1b6b3d06ddc1e..2123a3397f125 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -5852,8 +5852,7 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) const { T, VK, NTTP->getLocation()); if (NTTP->isParameterPack()) - E = new (*this) - PackExpansionExpr(DependentTy, E, NTTP->getLocation(), std::nullopt); + E = new (*this) PackExpansionExpr(E, NTTP->getLocation(), std::nullopt); Arg = TemplateArgument(E); } else { auto *TTP = cast(Param); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 8c91cce22f78e..f4b977d1d14b3 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8273,14 +8273,13 @@ ASTNodeImporter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { ExpectedStmt ASTNodeImporter::VisitPackExpansionExpr(PackExpansionExpr *E) { Error Err = Error::success(); - auto ToType = importChecked(Err, E->getType()); - auto ToPattern = importChecked(Err, E->getPattern()); + auto *ToPattern = importChecked(Err, E->getPattern()); auto ToEllipsisLoc = importChecked(Err, E->getEllipsisLoc()); if (Err) return std::move(Err); - return new (Importer.getToContext()) PackExpansionExpr( - ToType, ToPattern, ToEllipsisLoc, E->getNumExpansions()); + return new (Importer.getToContext()) + PackExpansionExpr(ToPattern, ToEllipsisLoc, E->getNumExpansions()); } ExpectedStmt ASTNodeImporter::VisitSizeOfPackExpr(SizeOfPackExpr *E) { diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 0564557738170..f46ef2c7f5bd6 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6201,7 +6201,7 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From, Sema::CCEKind CCE, NamedDecl *Dest, APValue &PreNarrowingValue) { - assert((S.getLangOpts().CPlusPlus11 || CCE == Sema::CCEK_InjectedTTP) && + assert((S.getLangOpts().CPlusPlus11 || CCE == Sema::CCEK_TempArgStrict) && "converted constant expression outside C++11 or TTP matching"); if (checkPlaceholderForOverload(S, From)) @@ -6272,7 +6272,7 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From, // class type. ExprResult Result; bool IsTemplateArgument = - CCE == Sema::CCEK_TemplateArg || CCE == Sema::CCEK_InjectedTTP; + CCE == Sema::CCEK_TemplateArg || CCE == Sema::CCEK_TempArgStrict; if (T->isRecordType()) { assert(IsTemplateArgument && "unexpected class type converted constant expr"); @@ -6325,7 +6325,7 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From, // value-dependent so we can't tell whether it's actually narrowing. // For matching the parameters of a TTP, the conversion is ill-formed // if it may narrow. - if (CCE != Sema::CCEK_InjectedTTP) + if (CCE != Sema::CCEK_TempArgStrict) break; [[fallthrough]]; case NK_Type_Narrowing: @@ -6400,7 +6400,7 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, Expr::EvalResult Eval; Eval.Diag = &Notes; - assert(CCE != Sema::CCEK_InjectedTTP && "unnexpected CCE Kind"); + assert(CCE != Sema::CCEK_TempArgStrict && "unnexpected CCE Kind"); ConstantExprKind Kind; if (CCE == Sema::CCEK_TemplateArg && T->isRecordType()) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 153f44f8ec67a..3d7fa38272e6a 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5263,9 +5263,9 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, auto checkExpr = [&](Expr *E) -> Expr * { TemplateArgument SugaredResult, CanonicalResult; unsigned CurSFINAEErrors = NumSFINAEErrors; - ExprResult Res = - CheckTemplateArgument(NTTP, NTTPType, E, SugaredResult, - CanonicalResult, CTAI.MatchingTTP, CTAK); + ExprResult Res = CheckTemplateArgument( + NTTP, NTTPType, E, SugaredResult, CanonicalResult, + /*StrictCheck=*/CTAI.MatchingTTP || CTAI.PartialOrdering, CTAK); // If the current template argument causes an error, give up now. if (Res.isInvalid() || CurSFINAEErrors < NumSFINAEErrors) return nullptr; @@ -5344,9 +5344,9 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, } TemplateArgument SugaredResult, CanonicalResult; - E = CheckTemplateArgument(NTTP, NTTPType, E.get(), SugaredResult, - CanonicalResult, /*PartialOrderingTTP=*/false, - CTAK_Specified); + E = CheckTemplateArgument( + NTTP, NTTPType, E.get(), SugaredResult, CanonicalResult, + /*StrictCheck=*/CTAI.PartialOrdering, CTAK_Specified); if (E.isInvalid()) return true; @@ -6757,9 +6757,21 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *Arg, TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted, - bool PartialOrderingTTP, + bool StrictCheck, CheckTemplateArgumentKind CTAK) { SourceLocation StartLoc = Arg->getBeginLoc(); + auto *ArgPE = dyn_cast(Arg); + Expr *DeductionArg = ArgPE ? ArgPE->getPattern() : Arg; + auto setDeductionArg = [&](Expr *NewDeductionArg) { + DeductionArg = NewDeductionArg; + if (ArgPE) { + // Recreate a pack expansion if we unwrapped one. + Arg = new (Context) PackExpansionExpr( + DeductionArg, ArgPE->getEllipsisLoc(), ArgPE->getNumExpansions()); + } else { + Arg = DeductionArg; + } + }; // If the parameter type somehow involves auto, deduce the type now. DeducedType *DeducedT = ParamType->getContainedDeducedType(); @@ -6769,7 +6781,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // FIXME: The language rules don't say what happens in this case. // FIXME: We get an opaque dependent type out of decltype(auto) if the // expression is merely instantiation-dependent; is this enough? - if (Arg->isTypeDependent()) { + if (DeductionArg->isTypeDependent()) { auto *AT = dyn_cast(DeducedT); if (AT && AT->isDecltypeAuto()) { SugaredConverted = TemplateArgument(Arg); @@ -6782,9 +6794,6 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // When checking a deduced template argument, deduce from its type even if // the type is dependent, in order to check the types of non-type template // arguments line up properly in partial ordering. - Expr *DeductionArg = Arg; - if (auto *PE = dyn_cast(DeductionArg)) - DeductionArg = PE->getPattern(); TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation()); if (isa(DeducedT)) { @@ -6837,64 +6846,55 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, assert(!ParamType.hasQualifiers() && "non-type template parameter type cannot be qualified"); + // If either the parameter has a dependent type or the argument is + // type-dependent, there's nothing we can check now. + if (ParamType->isDependentType() || DeductionArg->isTypeDependent()) { + // Force the argument to the type of the parameter to maintain invariants. + ExprResult E = ImpCastExprToType( + DeductionArg, ParamType.getNonLValueExprType(Context), CK_Dependent, + ParamType->isLValueReferenceType() ? VK_LValue + : ParamType->isRValueReferenceType() ? VK_XValue + : VK_PRValue); + if (E.isInvalid()) + return ExprError(); + setDeductionArg(E.get()); + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = TemplateArgument( + Context.getCanonicalTemplateArgument(SugaredConverted)); + return Arg; + } + // FIXME: When Param is a reference, should we check that Arg is an lvalue? - if (CTAK == CTAK_Deduced && + if (CTAK == CTAK_Deduced && !StrictCheck && (ParamType->isReferenceType() ? !Context.hasSameType(ParamType.getNonReferenceType(), - Arg->getType()) - : !Context.hasSameUnqualifiedType(ParamType, Arg->getType()))) { - // FIXME: If either type is dependent, we skip the check. This isn't - // correct, since during deduction we're supposed to have replaced each - // template parameter with some unique (non-dependent) placeholder. - // FIXME: If the argument type contains 'auto', we carry on and fail the - // type check in order to force specific types to be more specialized than - // 'auto'. It's not clear how partial ordering with 'auto' is supposed to - // work. Similarly for CTAD, when comparing 'A' against 'A'. - if ((ParamType->isDependentType() || Arg->isTypeDependent()) && - !Arg->getType()->getContainedDeducedType()) { - SugaredConverted = TemplateArgument(Arg); - CanonicalConverted = TemplateArgument( - Context.getCanonicalTemplateArgument(SugaredConverted)); - return Arg; - } + DeductionArg->getType()) + : !Context.hasSameUnqualifiedType(ParamType, + DeductionArg->getType()))) { // FIXME: This attempts to implement C++ [temp.deduct.type]p17. Per DR1770, // we should actually be checking the type of the template argument in P, // not the type of the template argument deduced from A, against the // template parameter type. Diag(StartLoc, diag::err_deduced_non_type_template_arg_type_mismatch) - << Arg->getType() - << ParamType.getUnqualifiedType(); + << Arg->getType() << ParamType.getUnqualifiedType(); NoteTemplateParameterLocation(*Param); return ExprError(); } - // If either the parameter has a dependent type or the argument is - // type-dependent, there's nothing we can check now. - if (ParamType->isDependentType() || Arg->isTypeDependent()) { - // Force the argument to the type of the parameter to maintain invariants. - auto *PE = dyn_cast(Arg); - if (PE) - Arg = PE->getPattern(); - ExprResult E = ImpCastExprToType( - Arg, ParamType.getNonLValueExprType(Context), CK_Dependent, - ParamType->isLValueReferenceType() ? VK_LValue - : ParamType->isRValueReferenceType() ? VK_XValue - : VK_PRValue); - if (E.isInvalid()) - return ExprError(); - if (PE) { - // Recreate a pack expansion if we unwrapped one. - E = new (Context) - PackExpansionExpr(E.get()->getType(), E.get(), PE->getEllipsisLoc(), - PE->getNumExpansions()); - } - SugaredConverted = TemplateArgument(E.get()); + // If the argument is a pack expansion, we don't know how many times it would + // expand. If we continue checking the argument, this will make the template + // definition ill-formed if it would be ill-formed for any number of + // expansions during instantiation time. When partial ordering or matching + // template template parameters, this is exactly what we want. Otherwise, the + // normal template rules apply: we accept the template if it would be valid + // for any number of expansions (i.e. none). + if (ArgPE && !StrictCheck) { + SugaredConverted = TemplateArgument(Arg); CanonicalConverted = TemplateArgument( Context.getCanonicalTemplateArgument(SugaredConverted)); - return E; + return Arg; } - QualType CanonParamType = Context.getCanonicalType(ParamType); // Avoid making a copy when initializing a template parameter of class type // from a template parameter object of the same type. This is going beyond // the standard, but is required for soundness: in @@ -6903,15 +6903,15 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // // Similarly, don't inject a call to a copy constructor when initializing // from a template parameter of the same type. - Expr *InnerArg = Arg->IgnoreParenImpCasts(); + Expr *InnerArg = DeductionArg->IgnoreParenImpCasts(); if (ParamType->isRecordType() && isa(InnerArg) && Context.hasSameUnqualifiedType(ParamType, InnerArg->getType())) { NamedDecl *ND = cast(InnerArg)->getDecl(); if (auto *TPO = dyn_cast(ND)) { SugaredConverted = TemplateArgument(TPO, ParamType); - CanonicalConverted = - TemplateArgument(TPO->getCanonicalDecl(), CanonParamType); + CanonicalConverted = TemplateArgument(TPO->getCanonicalDecl(), + ParamType.getCanonicalType()); return Arg; } if (isa(ND)) { @@ -6928,10 +6928,10 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, *this, Sema::ExpressionEvaluationContext::ConstantEvaluated); bool IsConvertedConstantExpression = true; - if (isa(Arg) || ParamType->isRecordType()) { + if (isa(DeductionArg) || ParamType->isRecordType()) { InitializationKind Kind = InitializationKind::CreateForInit( - Arg->getBeginLoc(), /*DirectInit=*/false, Arg); - Expr *Inits[1] = {Arg}; + StartLoc, /*DirectInit=*/false, DeductionArg); + Expr *Inits[1] = {DeductionArg}; InitializedEntity Entity = InitializedEntity::InitializeTemplateParameter(ParamType, Param); InitializationSequence InitSeq(*this, Entity, Kind, Inits); @@ -6941,14 +6941,15 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, Result = ActOnConstantExpression(Result.get()); if (Result.isInvalid() || !Result.get()) return ExprError(); - Arg = ActOnFinishFullExpr(Result.get(), Arg->getBeginLoc(), - /*DiscardedValue=*/false, - /*IsConstexpr=*/true, /*IsTemplateArgument=*/true) - .get(); + setDeductionArg(ActOnFinishFullExpr(Result.get(), Arg->getBeginLoc(), + /*DiscardedValue=*/false, + /*IsConstexpr=*/true, + /*IsTemplateArgument=*/true) + .get()); IsConvertedConstantExpression = false; } - if (getLangOpts().CPlusPlus17 || PartialOrderingTTP) { + if (getLangOpts().CPlusPlus17 || StrictCheck) { // C++17 [temp.arg.nontype]p1: // A template-argument for a non-type template parameter shall be // a converted constant expression of the type of the template-parameter. @@ -6956,24 +6957,25 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, ExprResult ArgResult; if (IsConvertedConstantExpression) { ArgResult = BuildConvertedConstantExpression( - Arg, ParamType, - PartialOrderingTTP ? CCEK_InjectedTTP : CCEK_TemplateArg, Param); + DeductionArg, ParamType, + StrictCheck ? CCEK_TempArgStrict : CCEK_TemplateArg, Param); assert(!ArgResult.isUnset()); if (ArgResult.isInvalid()) { NoteTemplateParameterLocation(*Param); return ExprError(); } } else { - ArgResult = Arg; + ArgResult = DeductionArg; } // For a value-dependent argument, CheckConvertedConstantExpression is // permitted (and expected) to be unable to determine a value. if (ArgResult.get()->isValueDependent()) { - SugaredConverted = TemplateArgument(ArgResult.get()); + setDeductionArg(ArgResult.get()); + SugaredConverted = TemplateArgument(Arg); CanonicalConverted = Context.getCanonicalTemplateArgument(SugaredConverted); - return ArgResult; + return Arg; } APValue PreNarrowingValue; @@ -6982,6 +6984,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, false, PreNarrowingValue); if (ArgResult.isInvalid()) return ExprError(); + setDeductionArg(ArgResult.get()); if (Value.isLValue()) { APValue::LValueBase Base = Value.getLValueBase(); @@ -7006,10 +7009,17 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, VD->getType()->isArrayType() && Value.getLValuePath()[0].getAsArrayIndex() == 0 && !Value.isLValueOnePastTheEnd() && ParamType->isPointerType()) { - SugaredConverted = TemplateArgument(VD, ParamType); - CanonicalConverted = TemplateArgument( - cast(VD->getCanonicalDecl()), CanonParamType); - return ArgResult.get(); + if (ArgPE) { + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = + Context.getCanonicalTemplateArgument(SugaredConverted); + } else { + SugaredConverted = TemplateArgument(VD, ParamType); + CanonicalConverted = + TemplateArgument(cast(VD->getCanonicalDecl()), + ParamType.getCanonicalType()); + } + return Arg; } // -- a subobject [until C++20] @@ -7030,9 +7040,16 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, if (Value.isAddrLabelDiff()) return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff); - SugaredConverted = TemplateArgument(Context, ParamType, Value); - CanonicalConverted = TemplateArgument(Context, CanonParamType, Value); - return ArgResult.get(); + if (ArgPE) { + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = + Context.getCanonicalTemplateArgument(SugaredConverted); + } else { + SugaredConverted = TemplateArgument(Context, ParamType, Value); + CanonicalConverted = + TemplateArgument(Context, ParamType.getCanonicalType(), Value); + } + return Arg; } // C++ [temp.arg.nontype]p5: @@ -7061,18 +7078,18 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // type, a converted constant expression of the type of the // template-parameter; or llvm::APSInt Value; - ExprResult ArgResult = - CheckConvertedConstantExpression(Arg, ParamType, Value, - CCEK_TemplateArg); + ExprResult ArgResult = CheckConvertedConstantExpression( + DeductionArg, ParamType, Value, CCEK_TemplateArg); if (ArgResult.isInvalid()) return ExprError(); + setDeductionArg(ArgResult.get()); // We can't check arbitrary value-dependent arguments. - if (ArgResult.get()->isValueDependent()) { - SugaredConverted = TemplateArgument(ArgResult.get()); + if (DeductionArg->isValueDependent()) { + SugaredConverted = TemplateArgument(Arg); CanonicalConverted = Context.getCanonicalTemplateArgument(SugaredConverted); - return ArgResult; + return Arg; } // Widen the argument value to sizeof(parameter type). This is almost @@ -7085,18 +7102,24 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, ? Context.getIntWidth(IntegerType) : Context.getTypeSize(IntegerType)); - SugaredConverted = TemplateArgument(Context, Value, ParamType); - CanonicalConverted = - TemplateArgument(Context, Value, Context.getCanonicalType(ParamType)); - return ArgResult; + if (ArgPE) { + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = + Context.getCanonicalTemplateArgument(SugaredConverted); + } else { + SugaredConverted = TemplateArgument(Context, Value, ParamType); + CanonicalConverted = TemplateArgument( + Context, Value, Context.getCanonicalType(ParamType)); + } + return Arg; } ExprResult ArgResult = DefaultLvalueConversion(Arg); if (ArgResult.isInvalid()) return ExprError(); - Arg = ArgResult.get(); + DeductionArg = ArgResult.get(); - QualType ArgType = Arg->getType(); + QualType ArgType = DeductionArg->getType(); // C++ [temp.arg.nontype]p1: // A template-argument for a non-type, non-template @@ -7107,11 +7130,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // -- the name of a non-type template-parameter; or llvm::APSInt Value; if (!ArgType->isIntegralOrEnumerationType()) { - Diag(Arg->getBeginLoc(), diag::err_template_arg_not_integral_or_enumeral) - << ArgType << Arg->getSourceRange(); + Diag(StartLoc, diag::err_template_arg_not_integral_or_enumeral) + << ArgType << DeductionArg->getSourceRange(); NoteTemplateParameterLocation(*Param); return ExprError(); - } else if (!Arg->isValueDependent()) { + } else if (!DeductionArg->isValueDependent()) { class TmplArgICEDiagnoser : public VerifyICEDiagnoser { QualType T; @@ -7124,8 +7147,10 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, } } Diagnoser(ArgType); - Arg = VerifyIntegerConstantExpression(Arg, &Value, Diagnoser).get(); - if (!Arg) + DeductionArg = + VerifyIntegerConstantExpression(DeductionArg, &Value, Diagnoser) + .get(); + if (!DeductionArg) return ExprError(); } @@ -7138,23 +7163,28 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // Okay: no conversion necessary } else if (ParamType->isBooleanType()) { // This is an integral-to-boolean conversion. - Arg = ImpCastExprToType(Arg, ParamType, CK_IntegralToBoolean).get(); + DeductionArg = + ImpCastExprToType(DeductionArg, ParamType, CK_IntegralToBoolean) + .get(); } else if (IsIntegralPromotion(Arg, ArgType, ParamType) || !ParamType->isEnumeralType()) { // This is an integral promotion or conversion. - Arg = ImpCastExprToType(Arg, ParamType, CK_IntegralCast).get(); + DeductionArg = + ImpCastExprToType(DeductionArg, ParamType, CK_IntegralCast).get(); } else { // We can't perform this conversion. - Diag(Arg->getBeginLoc(), diag::err_template_arg_not_convertible) - << Arg->getType() << ParamType << Arg->getSourceRange(); + Diag(StartLoc, diag::err_template_arg_not_convertible) + << DeductionArg->getType() << ParamType + << DeductionArg->getSourceRange(); NoteTemplateParameterLocation(*Param); return ExprError(); } + setDeductionArg(DeductionArg); // Add the value of this argument to the list of converted // arguments. We use the bitwidth and signedness of the template // parameter. - if (Arg->isValueDependent()) { + if (DeductionArg->isValueDependent()) { // The argument is value-dependent. Create a new // TemplateArgument with the converted expression. SugaredConverted = TemplateArgument(Arg); @@ -7212,14 +7242,20 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, } } - QualType T = ParamType->isEnumeralType() ? ParamType : IntegerType; - SugaredConverted = TemplateArgument(Context, Value, T); - CanonicalConverted = - TemplateArgument(Context, Value, Context.getCanonicalType(T)); + if (ArgPE) { + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = + Context.getCanonicalTemplateArgument(SugaredConverted); + } else { + QualType T = ParamType->isEnumeralType() ? ParamType : IntegerType; + SugaredConverted = TemplateArgument(Context, Value, T); + CanonicalConverted = + TemplateArgument(Context, Value, Context.getCanonicalType(T)); + } return Arg; } - QualType ArgType = Arg->getType(); + QualType ArgType = DeductionArg->getType(); DeclAccessPair FoundResult; // temporary for ResolveOverloadedFunction // Handle pointer-to-function, reference-to-function, and @@ -7246,7 +7282,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, ParamType->castAs()->getPointeeType() ->isFunctionType())) { - if (Arg->getType() == Context.OverloadTy) { + if (DeductionArg->getType() == Context.OverloadTy) { if (FunctionDecl *Fn = ResolveAddressOfOverloadedFunction(Arg, ParamType, true, FoundResult)) { @@ -7256,11 +7292,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, ExprResult Res = FixOverloadedFunctionReference(Arg, FoundResult, Fn); if (Res.isInvalid()) return ExprError(); - Arg = Res.get(); + DeductionArg = Res.get(); ArgType = Arg->getType(); } else return ExprError(); } + setDeductionArg(DeductionArg); if (!ParamType->isMemberPointerType()) { if (CheckTemplateArgumentAddressOfObjectOrFunction( @@ -7276,6 +7313,8 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return Arg; } + setDeductionArg(DeductionArg); + if (ParamType->isPointerType()) { // -- for a non-type template-parameter of type pointer to // object, qualification conversions (4.4) and the @@ -7284,6 +7323,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, assert(ParamType->getPointeeType()->isIncompleteOrObjectType() && "Only object pointers allowed here"); + // FIXME: Deal with pack expansions here. if (CheckTemplateArgumentAddressOfObjectOrFunction( *this, Param, ParamType, Arg, SugaredConverted, CanonicalConverted)) return ExprError(); @@ -7300,6 +7340,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, assert(ParamRefType->getPointeeType()->isIncompleteOrObjectType() && "Only object references allowed here"); + // FIXME: Deal with pack expansions here. if (Arg->getType() == Context.OverloadTy) { if (FunctionDecl *Fn = ResolveAddressOfOverloadedFunction(Arg, ParamRefType->getPointeeType(), @@ -7324,17 +7365,18 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // Deal with parameters of type std::nullptr_t. if (ParamType->isNullPtrType()) { - if (Arg->isTypeDependent() || Arg->isValueDependent()) { + if (DeductionArg->isTypeDependent() || DeductionArg->isValueDependent()) { SugaredConverted = TemplateArgument(Arg); CanonicalConverted = Context.getCanonicalTemplateArgument(SugaredConverted); return Arg; } - switch (isNullPointerValueTemplateArgument(*this, Param, ParamType, Arg)) { + switch (isNullPointerValueTemplateArgument(*this, Param, ParamType, + DeductionArg)) { case NPV_NotNullPointer: Diag(Arg->getExprLoc(), diag::err_template_arg_not_convertible) - << Arg->getType() << ParamType; + << DeductionArg->getType() << ParamType; NoteTemplateParameterLocation(*Param); return ExprError(); @@ -7343,10 +7385,17 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, case NPV_NullPointer: Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null); - SugaredConverted = TemplateArgument(ParamType, - /*isNullPtr=*/true); - CanonicalConverted = TemplateArgument(Context.getCanonicalType(ParamType), + if (ArgPE) { + SugaredConverted = TemplateArgument(Arg); + CanonicalConverted = + Context.getCanonicalTemplateArgument(SugaredConverted); + } else { + SugaredConverted = TemplateArgument(ParamType, /*isNullPtr=*/true); + CanonicalConverted = + TemplateArgument(Context.getCanonicalType(ParamType), + /*isNullPtr=*/true); + } return Arg; } } @@ -7355,6 +7404,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // member, qualification conversions (4.4) are applied. assert(ParamType->isMemberPointerType() && "Only pointers to members remain"); + // FIXME: Deal with pack expansions here. if (CheckTemplateArgumentPointerToMember( *this, Param, ParamType, Arg, SugaredConverted, CanonicalConverted)) return ExprError(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index ab6e18aee7206..8e8112610781f 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2826,16 +2826,8 @@ TemplateDeductionResult Sema::DeduceTemplateArguments( } /// Determine whether two template arguments are the same. -static bool isSameTemplateArg(ASTContext &Context, - TemplateArgument X, - const TemplateArgument &Y, - bool PartialOrdering, - bool PackExpansionMatchesPack = false) { - // If we're checking deduced arguments (X) against original arguments (Y), - // we will have flattened packs to non-expansions in X. - if (PackExpansionMatchesPack && X.isPackExpansion() && !Y.isPackExpansion()) - X = X.getPackExpansionPattern(); - +static bool isSameTemplateArg(ASTContext &Context, const TemplateArgument &X, + const TemplateArgument &Y) { if (X.getKind() != Y.getKind()) return false; @@ -2875,28 +2867,12 @@ static bool isSameTemplateArg(ASTContext &Context, case TemplateArgument::Pack: { unsigned PackIterationSize = X.pack_size(); - if (X.pack_size() != Y.pack_size()) { - if (!PartialOrdering) - return false; - - // C++0x [temp.deduct.type]p9: - // During partial ordering, if Ai was originally a pack expansion: - // - if P does not contain a template argument corresponding to Ai - // then Ai is ignored; - bool XHasMoreArg = X.pack_size() > Y.pack_size(); - if (!(XHasMoreArg && X.pack_elements().back().isPackExpansion()) && - !(!XHasMoreArg && Y.pack_elements().back().isPackExpansion())) - return false; - - if (XHasMoreArg) - PackIterationSize = Y.pack_size(); - } - + if (X.pack_size() != Y.pack_size()) + return false; ArrayRef XP = X.pack_elements(); ArrayRef YP = Y.pack_elements(); for (unsigned i = 0; i < PackIterationSize; ++i) - if (!isSameTemplateArg(Context, XP[i], YP[i], PartialOrdering, - PackExpansionMatchesPack)) + if (!isSameTemplateArg(Context, XP[i], YP[i])) return false; return true; } @@ -3074,22 +3050,16 @@ ConvertDeducedTemplateArgument(Sema &S, NamedDecl *Param, return ConvertArg(Arg, 0); } -// FIXME: This should not be a template, but -// ClassTemplatePartialSpecializationDecl sadly does not derive from -// TemplateDecl. /// \param IsIncomplete When used, we only consider template parameters that /// were deduced, disregarding any default arguments. After the function /// finishes, the object pointed at will contain a value indicating if the /// conversion was actually incomplete. -template static TemplateDeductionResult ConvertDeducedTemplateArguments( - Sema &S, TemplateDeclT *Template, bool IsDeduced, - SmallVectorImpl &Deduced, + Sema &S, NamedDecl *Template, TemplateParameterList *TemplateParams, + bool IsDeduced, SmallVectorImpl &Deduced, TemplateDeductionInfo &Info, Sema::CheckTemplateArgumentInfo &CTAI, LocalInstantiationScope *CurrentInstantiationScope, unsigned NumAlreadyConverted, bool *IsIncomplete) { - TemplateParameterList *TemplateParams = Template->getTemplateParameters(); - for (unsigned I = 0, N = TemplateParams->size(); I != N; ++I) { NamedDecl *Param = TemplateParams->getParam(I); @@ -3234,34 +3204,30 @@ template<> struct IsPartialSpecialization { static constexpr bool value = true; }; -template -static bool DeducedArgsNeedReplacement(TemplateDeclT *Template) { - return false; -} -template <> -bool DeducedArgsNeedReplacement( - VarTemplatePartialSpecializationDecl *Spec) { - return !Spec->isClassScopeExplicitSpecialization(); -} -template <> -bool DeducedArgsNeedReplacement( - ClassTemplatePartialSpecializationDecl *Spec) { - return !Spec->isClassScopeExplicitSpecialization(); -} -template static TemplateDeductionResult -CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template, +CheckDeducedArgumentConstraints(Sema &S, NamedDecl *Template, ArrayRef SugaredDeducedArgs, ArrayRef CanonicalDeducedArgs, TemplateDeductionInfo &Info) { llvm::SmallVector AssociatedConstraints; - Template->getAssociatedConstraints(AssociatedConstraints); + bool DeducedArgsNeedReplacement = false; + if (auto *TD = dyn_cast(Template)) { + TD->getAssociatedConstraints(AssociatedConstraints); + DeducedArgsNeedReplacement = !TD->isClassScopeExplicitSpecialization(); + } else if (auto *TD = + dyn_cast(Template)) { + TD->getAssociatedConstraints(AssociatedConstraints); + DeducedArgsNeedReplacement = !TD->isClassScopeExplicitSpecialization(); + } else { + cast(Template)->getAssociatedConstraints( + AssociatedConstraints); + } std::optional> Innermost; // If we don't need to replace the deduced template arguments, // we can add them immediately as the inner-most argument list. - if (!DeducedArgsNeedReplacement(Template)) + if (!DeducedArgsNeedReplacement) Innermost = CanonicalDeducedArgs; MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs( @@ -3288,73 +3254,60 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template, return TemplateDeductionResult::Success; } -/// Complete template argument deduction for a partial specialization. -template -static std::enable_if_t::value, - TemplateDeductionResult> -FinishTemplateArgumentDeduction( - Sema &S, T *Partial, bool IsPartialOrdering, - ArrayRef TemplateArgs, +/// Complete template argument deduction. +static TemplateDeductionResult FinishTemplateArgumentDeduction( + Sema &S, NamedDecl *Entity, TemplateParameterList *EntityTPL, + TemplateDecl *Template, bool PartialOrdering, + ArrayRef Ps, ArrayRef As, SmallVectorImpl &Deduced, - TemplateDeductionInfo &Info) { + TemplateDeductionInfo &Info, bool CopyDeducedArgs) { // Unevaluated SFINAE context. EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap Trap(S); - Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Partial)); + Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Entity)); // C++ [temp.deduct.type]p2: // [...] or if any template argument remains neither deduced nor // explicitly specified, template argument deduction fails. - Sema::CheckTemplateArgumentInfo CTAI(IsPartialOrdering); + Sema::CheckTemplateArgumentInfo CTAI(PartialOrdering); if (auto Result = ConvertDeducedTemplateArguments( - S, Partial, IsPartialOrdering, Deduced, Info, CTAI, - /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, - /*IsIncomplete=*/nullptr); + S, Entity, EntityTPL, /*IsDeduced=*/PartialOrdering, Deduced, Info, + CTAI, + /*CurrentInstantiationScope=*/nullptr, + /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) return Result; - // Form the template argument list from the deduced template arguments. - TemplateArgumentList *SugaredDeducedArgumentList = - TemplateArgumentList::CreateCopy(S.Context, CTAI.SugaredConverted); - TemplateArgumentList *CanonicalDeducedArgumentList = - TemplateArgumentList::CreateCopy(S.Context, CTAI.CanonicalConverted); + if (CopyDeducedArgs) { + // Form the template argument list from the deduced template arguments. + TemplateArgumentList *SugaredDeducedArgumentList = + TemplateArgumentList::CreateCopy(S.Context, CTAI.SugaredConverted); + TemplateArgumentList *CanonicalDeducedArgumentList = + TemplateArgumentList::CreateCopy(S.Context, CTAI.CanonicalConverted); + Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList); + } - Info.reset(SugaredDeducedArgumentList, CanonicalDeducedArgumentList); + TemplateParameterList *TPL = Template->getTemplateParameters(); + TemplateArgumentListInfo InstArgs(TPL->getLAngleLoc(), TPL->getRAngleLoc()); + MultiLevelTemplateArgumentList MLTAL(Entity, CTAI.SugaredConverted, + /*Final=*/true); + MLTAL.addOuterRetainedLevels(TPL->getDepth()); - // Substitute the deduced template arguments into the template - // arguments of the class template partial specialization, and - // verify that the instantiated template arguments are both valid - // and are equivalent to the template arguments originally provided - // to the class template. - LocalInstantiationScope InstScope(S); - auto *Template = Partial->getSpecializedTemplate(); - const ASTTemplateArgumentListInfo *PartialTemplArgInfo = - Partial->getTemplateArgsAsWritten(); - - TemplateArgumentListInfo InstArgs(PartialTemplArgInfo->LAngleLoc, - PartialTemplArgInfo->RAngleLoc); - - if (S.SubstTemplateArguments( - PartialTemplArgInfo->arguments(), - MultiLevelTemplateArgumentList(Partial, CTAI.SugaredConverted, - /*Final=*/true), - InstArgs)) { + if (S.SubstTemplateArguments(Ps, MLTAL, InstArgs)) { unsigned ArgIdx = InstArgs.size(), ParamIdx = ArgIdx; - if (ParamIdx >= Partial->getTemplateParameters()->size()) - ParamIdx = Partial->getTemplateParameters()->size() - 1; + if (ParamIdx >= TPL->size()) + ParamIdx = TPL->size() - 1; - Decl *Param = const_cast( - Partial->getTemplateParameters()->getParam(ParamIdx)); + Decl *Param = const_cast(TPL->getParam(ParamIdx)); Info.Param = makeTemplateParameter(Param); - Info.FirstArg = (*PartialTemplArgInfo)[ArgIdx].getArgument(); + Info.FirstArg = Ps[ArgIdx].getArgument(); return TemplateDeductionResult::SubstitutionFailure; } bool ConstraintsNotSatisfied; Sema::CheckTemplateArgumentInfo InstCTAI; - if (S.CheckTemplateArgumentList(Template, Partial->getLocation(), InstArgs, + if (S.CheckTemplateArgumentList(Template, Template->getLocation(), InstArgs, /*DefaultArgs=*/{}, false, InstCTAI, /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied)) @@ -3362,59 +3315,9 @@ FinishTemplateArgumentDeduction( ? TemplateDeductionResult::ConstraintsNotSatisfied : TemplateDeductionResult::SubstitutionFailure; - TemplateParameterList *TemplateParams = Template->getTemplateParameters(); - for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) { - TemplateArgument InstArg = InstCTAI.SugaredConverted.data()[I]; - if (!isSameTemplateArg(S.Context, TemplateArgs[I], InstArg, - IsPartialOrdering)) { - Info.Param = makeTemplateParameter(TemplateParams->getParam(I)); - Info.FirstArg = TemplateArgs[I]; - Info.SecondArg = InstArg; - return TemplateDeductionResult::NonDeducedMismatch; - } - } - - if (Trap.hasErrorOccurred()) - return TemplateDeductionResult::SubstitutionFailure; - - if (!IsPartialOrdering) { - if (auto Result = CheckDeducedArgumentConstraints( - S, Partial, CTAI.SugaredConverted, CTAI.CanonicalConverted, Info); - Result != TemplateDeductionResult::Success) - return Result; - } - - return TemplateDeductionResult::Success; -} - -/// Complete template argument deduction for a class or variable template, -/// when partial ordering against a partial specialization. -// FIXME: Factor out duplication with partial specialization version above. -static TemplateDeductionResult FinishTemplateArgumentDeduction( - Sema &S, TemplateDecl *Template, bool PartialOrdering, - ArrayRef TemplateArgs, - SmallVectorImpl &Deduced, - TemplateDeductionInfo &Info) { - // Unevaluated SFINAE context. - EnterExpressionEvaluationContext Unevaluated( - S, Sema::ExpressionEvaluationContext::Unevaluated); - - Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Template)); - - // C++ [temp.deduct.type]p2: - // [...] or if any template argument remains neither deduced nor - // explicitly specified, template argument deduction fails. - Sema::CheckTemplateArgumentInfo CTAI(PartialOrdering); - if (auto Result = ConvertDeducedTemplateArguments( - S, Template, /*IsDeduced=*/PartialOrdering, Deduced, Info, CTAI, - /*CurrentInstantiationScope=*/nullptr, - /*NumAlreadyConverted=*/0U, /*IsIncomplete=*/nullptr); - Result != TemplateDeductionResult::Success) - return Result; - // Check that we produced the correct argument list. - SmallVector, 4> PsStack{TemplateArgs}, - AsStack{CTAI.CanonicalConverted}; + SmallVector, 4> PsStack{InstCTAI.SugaredConverted}, + AsStack{As}; for (;;) { auto take = [](SmallVectorImpl> &Stack) -> std::tuple &, TemplateArgument> { @@ -3443,13 +3346,11 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( break; TemplateArgument PP = P.isPackExpansion() ? P.getPackExpansionPattern() : P, PA = A.isPackExpansion() ? A.getPackExpansionPattern() : A; - if (!isSameTemplateArg(S.Context, PP, PA, /*PartialOrdering=*/false)) { + if (!isSameTemplateArg(S.Context, PP, PA)) { if (!P.isPackExpansion() && !A.isPackExpansion()) { - Info.Param = - makeTemplateParameter(Template->getTemplateParameters()->getParam( - (AsStack.empty() ? CTAI.CanonicalConverted.end() - : AsStack.front().begin()) - - 1 - CTAI.CanonicalConverted.begin())); + Info.Param = makeTemplateParameter(TPL->getParam( + (AsStack.empty() ? As.end() : AsStack.back().begin()) - + As.begin())); Info.FirstArg = P; Info.SecondArg = A; return TemplateDeductionResult::NonDeducedMismatch; @@ -3471,13 +3372,28 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( if (!PartialOrdering) { if (auto Result = CheckDeducedArgumentConstraints( - S, Template, CTAI.SugaredConverted, CTAI.CanonicalConverted, Info); + S, Entity, CTAI.SugaredConverted, CTAI.CanonicalConverted, Info); Result != TemplateDeductionResult::Success) return Result; } return TemplateDeductionResult::Success; } +static TemplateDeductionResult FinishTemplateArgumentDeduction( + Sema &S, NamedDecl *Entity, TemplateParameterList *EntityTPL, + TemplateDecl *Template, bool PartialOrdering, ArrayRef Ps, + ArrayRef As, + SmallVectorImpl &Deduced, + TemplateDeductionInfo &Info, bool CopyDeducedArgs) { + TemplateParameterList *TPL = Template->getTemplateParameters(); + SmallVector PsLoc(Ps.size()); + for (unsigned I = 0, N = Ps.size(); I != N; ++I) + PsLoc[I] = S.getTrivialTemplateArgumentLoc(Ps[I], QualType(), + TPL->getParam(I)->getLocation()); + return FinishTemplateArgumentDeduction(S, Entity, EntityTPL, Template, + PartialOrdering, PsLoc, As, Deduced, + Info, CopyDeducedArgs); +} /// Complete template argument deduction for DeduceTemplateArgumentsFromType. /// FIXME: this is mostly duplicated with the above two versions. Deduplicate @@ -3497,7 +3413,8 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( // explicitly specified, template argument deduction fails. Sema::CheckTemplateArgumentInfo CTAI; if (auto Result = ConvertDeducedTemplateArguments( - S, TD, /*IsDeduced=*/false, Deduced, Info, CTAI, + S, TD, TD->getTemplateParameters(), /*IsDeduced=*/false, Deduced, + Info, CTAI, /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, /*IsIncomplete=*/nullptr); Result != TemplateDeductionResult::Success) @@ -3553,9 +3470,12 @@ DeduceTemplateArguments(Sema &S, T *Partial, TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { - Result = ::FinishTemplateArgumentDeduction(S, Partial, - /*IsPartialOrdering=*/false, - TemplateArgs, Deduced, Info); + Result = ::FinishTemplateArgumentDeduction( + S, Partial, Partial->getTemplateParameters(), + Partial->getSpecializedTemplate(), + /*IsPartialOrdering=*/false, + Partial->getTemplateArgsAsWritten()->arguments(), TemplateArgs, Deduced, + Info, /*CopyDeducedArgs=*/true); }); if (Result != TemplateDeductionResult::Success) @@ -4062,9 +3982,9 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( bool IsIncomplete = false; CheckTemplateArgumentInfo CTAI(PartialOrdering); if (auto Result = ConvertDeducedTemplateArguments( - *this, FunctionTemplate, /*IsDeduced=*/true, Deduced, Info, CTAI, - CurrentInstantiationScope, NumExplicitlySpecified, - PartialOverloading ? &IsIncomplete : nullptr); + *this, FunctionTemplate, FunctionTemplate->getTemplateParameters(), + /*IsDeduced=*/true, Deduced, Info, CTAI, CurrentInstantiationScope, + NumExplicitlySpecified, PartialOverloading ? &IsIncomplete : nullptr); Result != TemplateDeductionResult::Success) return Result; @@ -5677,7 +5597,8 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction( bool IsIncomplete = false; Sema::CheckTemplateArgumentInfo CTAI(/*PartialOrdering=*/true); if (auto Result = ConvertDeducedTemplateArguments( - S, FTD, /*IsDeduced=*/true, Deduced, Info, CTAI, + S, FTD, FTD->getTemplateParameters(), /*IsDeduced=*/true, Deduced, + Info, CTAI, /*CurrentInstantiationScope=*/nullptr, /*NumAlreadyConverted=*/0, &IsIncomplete); Result != TemplateDeductionResult::Success) @@ -6243,16 +6164,19 @@ FunctionDecl *Sema::getMoreConstrainedFunction(FunctionDecl *FD1, return AtLeastAsConstrained1 ? FD1 : FD2; } -/// Determine whether one partial specialization, P1, is at least as +/// Determine whether one template specialization, P1, is at least as /// specialized than another, P2. /// /// \tparam TemplateLikeDecl The kind of P2, which must be a /// TemplateDecl or {Class,Var}TemplatePartialSpecializationDecl. /// \param T1 The injected-class-name of P1 (faked for a variable template). /// \param T2 The injected-class-name of P2 (faked for a variable template). -template +/// \param Template The primary template of P2, in case it is a partial +/// specialization, the same as P2 otherwise. +template static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P2, + TemplateDecl *Template, TemplateDeductionInfo &Info) { // C++ [temp.class.order]p1: // For two class template partial specializations, the first is at least as @@ -6295,15 +6219,18 @@ static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2, if (Inst.isInvalid()) return false; - const auto *TST1 = cast(T1); + ArrayRef + Ps = cast(T2)->template_arguments(), + As = cast(T1)->template_arguments(); Sema::SFINAETrap Trap(S); TemplateDeductionResult Result; S.runWithSufficientStackSpace(Info.getLocation(), [&] { Result = ::FinishTemplateArgumentDeduction( - S, P2, /*IsPartialOrdering=*/true, TST1->template_arguments(), Deduced, - Info); + S, P2, P2->getTemplateParameters(), Template, + /*IsPartialOrdering=*/true, Ps, As, Deduced, Info, + /*CopyDeducedArgs=*/false); }); if (Result != TemplateDeductionResult::Success) @@ -6407,11 +6334,18 @@ getMoreSpecialized(Sema &S, QualType T1, QualType T2, TemplateLikeDecl *P1, constexpr bool IsMoreSpecialThanPrimaryCheck = !std::is_same_v; - bool Better1 = isAtLeastAsSpecializedAs(S, T1, T2, P2, Info); + TemplateDecl *P2T; + if constexpr (IsMoreSpecialThanPrimaryCheck) + P2T = P2; + else + P2T = P2->getSpecializedTemplate(); + + bool Better1 = isAtLeastAsSpecializedAs(S, T1, T2, P2, P2T, Info); if (IsMoreSpecialThanPrimaryCheck && !Better1) return nullptr; - bool Better2 = isAtLeastAsSpecializedAs(S, T2, T1, P1, Info); + bool Better2 = isAtLeastAsSpecializedAs(S, T2, T1, P1, + P1->getSpecializedTemplate(), Info); if (IsMoreSpecialThanPrimaryCheck && !Better2) return P1; @@ -6666,8 +6600,9 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs( TemplateDeductionResult TDK; runWithSufficientStackSpace(Info.getLocation(), [&] { - TDK = ::FinishTemplateArgumentDeduction(*this, AArg, PartialOrdering, PArgs, - Deduced, Info); + TDK = ::FinishTemplateArgumentDeduction( + *this, AArg, AArg->getTemplateParameters(), AArg, PartialOrdering, + AArgs, PArgs, Deduced, Info, /*CopyDeducedArgs=*/false); }); switch (TDK) { case TemplateDeductionResult::Success: diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index dd493a083d86d..d2408a94ad0ab 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2383,10 +2383,10 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmExpr( // The call to CheckTemplateArgument here produces the ImpCast. TemplateArgument SugaredConverted, CanonicalConverted; if (SemaRef - .CheckTemplateArgument( - E->getParameter(), SubstType, SubstReplacement.get(), - SugaredConverted, CanonicalConverted, - /*PartialOrderingTTP=*/false, Sema::CTAK_Specified) + .CheckTemplateArgument(E->getParameter(), SubstType, + SubstReplacement.get(), SugaredConverted, + CanonicalConverted, + /*StrictCheck=*/false, Sema::CTAK_Specified) .isInvalid()) return true; return transformNonTypeTemplateParmRef( diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index ef0e6ee23e942..3d4a245eb8bd5 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -745,8 +745,7 @@ ExprResult Sema::CheckPackExpansion(Expr *Pattern, SourceLocation EllipsisLoc, } // Create the pack expansion expression and source-location information. - return new (Context) - PackExpansionExpr(Context.DependentTy, Pattern, EllipsisLoc, NumExpansions); + return new (Context) PackExpansionExpr(Pattern, EllipsisLoc, NumExpansions); } bool Sema::CheckParameterPacksForExpansion( diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 237c5a9ef501b..87ec6ac7825ac 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -16128,8 +16128,7 @@ TreeTransform::TransformSizeOfPackExpr(SizeOfPackExpr *E) { if (DRE.isInvalid()) return ExprError(); ArgStorage = new (getSema().Context) - PackExpansionExpr(getSema().Context.DependentTy, DRE.get(), - E->getPackLoc(), std::nullopt); + PackExpansionExpr(DRE.get(), E->getPackLoc(), std::nullopt); } PackArgs = ArgStorage; } diff --git a/clang/test/Import/pack-expansion-expr/test.cpp b/clang/test/Import/pack-expansion-expr/test.cpp index 6866c41cfbcf4..3aa108b2ab897 100644 --- a/clang/test/Import/pack-expansion-expr/test.cpp +++ b/clang/test/Import/pack-expansion-expr/test.cpp @@ -1,7 +1,7 @@ // RUN: clang-import-test -dump-ast -import %S/Inputs/F.cpp -expression %s | FileCheck %s // CHECK: PackExpansionExpr -// CHECK-SAME: '' +// CHECK-SAME: 'T' // CHECK-NEXT: DeclRefExpr // CHECK-SAME: 'T' // CHECK-SAME: ParmVar diff --git a/clang/test/SemaTemplate/attributes.cpp b/clang/test/SemaTemplate/attributes.cpp index dea19d09745ca..020cfd291a502 100644 --- a/clang/test/SemaTemplate/attributes.cpp +++ b/clang/test/SemaTemplate/attributes.cpp @@ -80,7 +80,7 @@ void UseStmtAnnotations() { HasStmtAnnotations(); } // CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is // CHECK-NEXT: FunctionDecl {{.*}} HasPackAnnotations 'void ()' // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BAZ" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: FunctionDecl {{.*}} used HasPackAnnotations 'void ()' // CHECK-NEXT: TemplateArgument{{.*}} pack @@ -111,7 +111,7 @@ void UsePackAnnotations() { HasPackAnnotations<1, 2, 3>(); } // CHECK-NEXT: FunctionDecl {{.*}} HasStmtPackAnnotations 'void ()' // CHECK: AttributedStmt {{.*}} // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_QUUX" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK: FunctionDecl {{.*}} used HasStmtPackAnnotations 'void ()' // CHECK-NEXT: TemplateArgument{{.*}} pack @@ -152,7 +152,7 @@ void UseOnlyPackAnnotations() { // CHECK-NEXT: MoveAssignment // CHECK-NEXT: Destructor // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FOZ" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct // CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition @@ -285,7 +285,7 @@ void UseOnlyPackAnnotations() { // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOO" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct // CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition @@ -428,7 +428,7 @@ void UseAnnotatedPackTemplateStructSpecializations() { // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BIR" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct // CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition @@ -478,7 +478,7 @@ void UseInvalidAnnotatedPackTemplateStruct() { // CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is // CHECK-NEXT: FunctionDecl {{.*}} RedeclaredAnnotatedFunc 'void ()' // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FAR" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: FunctionDecl {{.*}} used RedeclaredAnnotatedFunc 'void ()' // CHECK-NEXT: TemplateArgument{{.*}} pack @@ -517,20 +517,20 @@ void UseInvalidAnnotatedPackTemplateStruct() { // CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is // CHECK-NEXT: FunctionDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc 'void ()' // CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOZ" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: Function {{.*}} 'RedeclaredAnnotatedFunc' 'void ()' // CHECK-NEXT: FunctionTemplateDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc // CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} 'int' depth 0 index 0 ... Is // CHECK-NEXT: FunctionDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc 'void ()' // CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_BOZ" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FIZ" // CHECK-NEXT: ConstantExpr {{.*}} 'int' @@ -545,7 +545,7 @@ void UseInvalidAnnotatedPackTemplateStruct() { // CHECK-NEXT: FunctionDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc 'void ()' // CHECK-NEXT: CompoundStmt // CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" -// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' // CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' // CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FIZ" // CHECK-NEXT: ConstantExpr {{.*}} 'int' diff --git a/clang/test/SemaTemplate/partial-order.cpp b/clang/test/SemaTemplate/partial-order.cpp index 0a151de390236..db2624d1766bc 100644 --- a/clang/test/SemaTemplate/partial-order.cpp +++ b/clang/test/SemaTemplate/partial-order.cpp @@ -1,6 +1,4 @@ -// RUN: %clang_cc1 -std=c++1z %s -verify - -// expected-no-diagnostics +// RUN: %clang_cc1 -std=c++26 %s -verify namespace hana_enable_if_idiom { template struct A {}; @@ -12,3 +10,40 @@ namespace hana_enable_if_idiom { }; B b; } + +namespace GH132562 { + struct I { + int v = 0; + }; + + namespace t1 { + template struct A; + template + requires ((X.v == 0) ||...) + struct A; + } // namespace t1 + namespace t2 { + template struct A; // expected-note {{template is declared here}} + template struct A; + // expected-error@-1 {{is not more specialized than the primary template}} + // expected-note@-2 {{no viable conversion from 'int' to 'I'}} + + template struct B; // expected-note {{template is declared here}} + template struct B; + // expected-error@-1 {{is not more specialized than the primary template}} + // expected-note@-2 {{value of type 'const I' is not implicitly convertible to 'int'}} + } // namespace t2 + namespace t3 { + struct J { + int v = 0; + constexpr J(int v) : v(v) {} + }; + template struct A; + template struct A; + + template struct B; // expected-note {{template is declared here}} + template struct B; + // expected-error@-1 {{is not more specialized than the primary template}} + // expected-note@-2 {{value of type 'const J' is not implicitly convertible to 'int'}} + } // namespace t3 +} // namespace GH132562 diff --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp b/clang/test/SemaTemplate/temp_arg_nontype.cpp index e989e45efb687..9363e748c7028 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype.cpp @@ -389,10 +389,13 @@ namespace PR17696 { namespace partial_order_different_types { template struct A; // expected-note@-1 {{template is declared here}} - template struct A<0, N, T, U, V> {}; - template struct A<0, N, T, U, V>; + template struct A<0, N, T, U, V> {}; // #P1 + template struct A<0, N, T, U, V>; // #P2 // expected-error@-1 {{class template partial specialization is not more specialized than the primary template}} A<0, 0, int, int, 0> a; + // expected-error@-1 {{ambiguous partial specializations}} + // expected-note@#P1 {{partial specialization matches}} + // expected-note@#P2 {{partial specialization matches}} } namespace partial_order_references { @@ -412,19 +415,18 @@ namespace partial_order_references { template struct B; // expected-note 2{{template}} template struct B<0, R> {}; // expected-error@-1 {{not more specialized than the primary}} - // expected-note@-2 {{'const int' vs 'int &'}} + // expected-note@-2 {{value of type 'const int' is not implicitly convertible to 'int &'}} B<0, N> b; // expected-error {{undefined}} - template struct C; // expected-note 2{{template}} + template struct C; // expected-note {{template}} + // This partial specialization is more specialized than the primary template. template struct C<0, R> {}; - // expected-error@-1 {{not more specialized than the primary}} - // expected-note@-2 {{'int' vs 'const int &'}} C<0, N> c; // expected-error {{undefined}} template struct D; // expected-note 2{{template}} template struct D<0, N> {}; // expected-error@-1 {{not more specialized than the primary}} - // expected-note@-2 {{'int' vs 'const int &'}} + // expected-note@-2 {{conversion from 'int' to 'const int &'}} extern const int K = 5; D<0, K> d; // expected-error {{undefined}} }