Skip to content

Commit 7c84cd6

Browse files
committed
[clang] Finish implementation of P0522
This finishes the clang implementation of P0522, getting rid of the fallback to the old, pre-P0522 rules. Before this patch, when partial ordering template template parameters, we would perform, in order: * If the old rules would match, we would accept it. Otherwise, don't generate diagnostics yet. * If the new rules would match, just accept it. Otherwise, don't generate any diagnostics yet again. * Apply the old rules again, this time with diagnostics. This situation was far from ideal, as we would sometimes: * Accept some things we shouldn't. * Reject some things we shouldn't. * Only diagnose rejection in terms of the old rules. With this patch, we apply the P0522 rules throughout. This needed to extend template argument deduction in order to accept the historial rule for TTP matching pack parameter to non-pack arguments. This change also makes us accept some combinations of historical and P0522 allowances we wouldn't before. It also fixes a bunch of bugs that were documented in the test suite, which I am not sure there are issues already created for them. This causes a lot of changes to the way these failures are diagnosed, with related test suite churn. The problem here is that the old rules were very simple and non-recursive, making it easy to provide customized diagnostics, and to keep them consistent with each other. The new rules are a lot more complex and rely on template argument deduction, substitutions, and they are recursive. The approach taken here is to mostly rely on existing diagnostics, and create a new instantiation context that keeps track of this context. So for example when a substitution failure occurs, we use the error produced there unmodified, and just attach notes to it explaining that it occurred in the context of partial ordering this template argument against that template parameter. This diverges from the old diagnostics, which would lead with an error pointing to the template argument, explain the problem in subsequent notes, and produce a final note pointing to the parameter.
1 parent b238961 commit 7c84cd6

18 files changed

+504
-280
lines changed

clang/docs/ReleaseNotes.rst

+8-1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ C++17 Feature Support
176176
the values produced by GCC, so these macros should not be used from header
177177
files because they may not be stable across multiple TUs (the values may vary
178178
based on compiler version as well as CPU tuning). #GH60174
179+
- The implementation of the relaxed template template argument matching rules is
180+
more complete and reliable, and should provide more accurate diagnostics.
179181

180182
C++14 Feature Support
181183
^^^^^^^^^^^^^^^^^^^^^
@@ -589,6 +591,10 @@ Improvements to Clang's diagnostics
589591
- Clang no longer emits a "declared here" note for a builtin function that has no declaration in source.
590592
Fixes #GH93369.
591593

594+
- Clang now properly explains the reason a template template argument failed to
595+
match a template template parameter, in terms of the C++17 relaxed matching rules
596+
instead of the old ones.
597+
592598
Improvements to Clang's time-trace
593599
----------------------------------
594600

@@ -887,7 +893,8 @@ Bug Fixes to C++ Support
887893
between the addresses of two labels (a GNU extension) to a pointer within a constant expression. (#GH95366).
888894
- Fix immediate escalation bugs in the presence of dependent call arguments. (#GH94935)
889895
- Clang now diagnoses explicit specializations with storage class specifiers in all contexts.
890-
896+
- Fixes to several issues in partial ordering of template template parameters, which
897+
were documented in the test suite.
891898

892899
Bug Fixes to AST Handling
893900
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticSemaKinds.td

+7
Original file line numberDiff line numberDiff line change
@@ -5207,6 +5207,13 @@ def note_template_arg_refers_here_func : Note<
52075207
def err_template_arg_template_params_mismatch : Error<
52085208
"template template argument has different template parameters than its "
52095209
"corresponding template template parameter">;
5210+
def note_template_arg_template_params_mismatch : Note<
5211+
"template template argument has different template parameters than its "
5212+
"corresponding template template parameter">;
5213+
def err_non_deduced_mismatch : Error<
5214+
"could not match %diff{$ against $|types}0,1">;
5215+
def err_inconsistent_deduction : Error<
5216+
"conflicting deduction %diff{$ against $|types}0,1 for parameter">;
52105217
def err_template_arg_not_integral_or_enumeral : Error<
52115218
"non-type template argument of type %0 must have an integral or enumeration"
52125219
" type">;

clang/include/clang/Sema/Sema.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -9728,8 +9728,9 @@ class Sema final : public SemaBase {
97289728
sema::TemplateDeductionInfo &Info);
97299729

97309730
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
9731-
TemplateParameterList *PParam, TemplateDecl *AArg,
9732-
const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced);
9731+
TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg,
9732+
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
9733+
bool IsDeduced);
97339734

97349735
void MarkUsedTemplateParameters(const Expr *E, bool OnlyDeduced,
97359736
unsigned Depth, llvm::SmallBitVector &Used);
@@ -9934,6 +9935,9 @@ class Sema final : public SemaBase {
99349935

99359936
/// We are instantiating a type alias template declaration.
99369937
TypeAliasTemplateInstantiation,
9938+
9939+
/// We are performing partial ordering for template template parameters.
9940+
PartialOrderTTP,
99379941
} Kind;
99389942

99399943
/// Was the enclosing context a non-instantiation SFINAE context?
@@ -10155,6 +10159,12 @@ class Sema final : public SemaBase {
1015510159
TemplateDecl *Entity, BuildingDeductionGuidesTag,
1015610160
SourceRange InstantiationRange = SourceRange());
1015710161

10162+
struct PartialOrderTTP {};
10163+
/// \brief Note that we are partial ordering template template parameters.
10164+
InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc, PartialOrderTTP,
10165+
TemplateDecl *PArg,
10166+
SourceRange InstantiationRange = SourceRange());
10167+
1015810168
/// Note that we have finished instantiating this template.
1015910169
void Clear();
1016010170

clang/lib/Frontend/FrontendActions.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
456456
return "BuildingDeductionGuides";
457457
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
458458
return "TypeAliasTemplateInstantiation";
459+
case CodeSynthesisContext::PartialOrderTTP:
460+
return "PartialOrderTTP";
459461
}
460462
return "";
461463
}

clang/lib/Sema/SemaTemplate.cpp

+38-56
Original file line numberDiff line numberDiff line change
@@ -6652,8 +6652,7 @@ bool Sema::CheckTemplateArgumentList(
66526652
DefaultArgs && ParamIdx >= DefaultArgs.StartPos) {
66536653
// All written arguments should have been consumed by this point.
66546654
assert(ArgIdx == NumArgs && "bad default argument deduction");
6655-
// FIXME: Don't ignore parameter packs.
6656-
if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) {
6655+
if (ParamIdx == DefaultArgs.StartPos) {
66576656
assert(Param + DefaultArgs.Args.size() <= ParamEnd);
66586657
// Default arguments from a DeducedTemplateName are already converted.
66596658
for (const TemplateArgument &DefArg : DefaultArgs.Args) {
@@ -6897,8 +6896,9 @@ bool Sema::CheckTemplateArgumentList(
68976896
// pack expansions; they might be empty. This can happen even if
68986897
// PartialTemplateArgs is false (the list of arguments is complete but
68996898
// still dependent).
6900-
if (ArgIdx < NumArgs && CurrentInstantiationScope &&
6901-
CurrentInstantiationScope->getPartiallySubstitutedPack()) {
6899+
if (PartialOrderingTTP ||
6900+
(CurrentInstantiationScope &&
6901+
CurrentInstantiationScope->getPartiallySubstitutedPack())) {
69026902
while (ArgIdx < NumArgs &&
69036903
NewArgs[ArgIdx].getArgument().isPackExpansion()) {
69046904
const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument();
@@ -8513,64 +8513,46 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
85138513
<< Template;
85148514
}
85158515

8516+
if (!getLangOpts().RelaxedTemplateTemplateArgs)
8517+
return !TemplateParameterListsAreEqual(
8518+
Template->getTemplateParameters(), Params, /*Complain=*/true,
8519+
TPL_TemplateTemplateArgumentMatch, Arg.getLocation());
8520+
85168521
// C++1z [temp.arg.template]p3: (DR 150)
85178522
// A template-argument matches a template template-parameter P when P
85188523
// is at least as specialized as the template-argument A.
8519-
if (getLangOpts().RelaxedTemplateTemplateArgs) {
8520-
// Quick check for the common case:
8521-
// If P contains a parameter pack, then A [...] matches P if each of A's
8522-
// template parameters matches the corresponding template parameter in
8523-
// the template-parameter-list of P.
8524-
if (TemplateParameterListsAreEqual(
8525-
Template->getTemplateParameters(), Params, false,
8526-
TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) &&
8527-
// If the argument has no associated constraints, then the parameter is
8528-
// definitely at least as specialized as the argument.
8529-
// Otherwise - we need a more thorough check.
8530-
!Template->hasAssociatedConstraints())
8531-
return false;
8532-
8533-
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(
8534-
Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) {
8535-
// P2113
8536-
// C++20[temp.func.order]p2
8537-
// [...] If both deductions succeed, the partial ordering selects the
8538-
// more constrained template (if one exists) as determined below.
8539-
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
8540-
Params->getAssociatedConstraints(ParamsAC);
8541-
// C++2a[temp.arg.template]p3
8542-
// [...] In this comparison, if P is unconstrained, the constraints on A
8543-
// are not considered.
8544-
if (ParamsAC.empty())
8545-
return false;
8524+
if (!isTemplateTemplateParameterAtLeastAsSpecializedAs(
8525+
Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced))
8526+
return true;
8527+
// P2113
8528+
// C++20[temp.func.order]p2
8529+
// [...] If both deductions succeed, the partial ordering selects the
8530+
// more constrained template (if one exists) as determined below.
8531+
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
8532+
Params->getAssociatedConstraints(ParamsAC);
8533+
// C++2a[temp.arg.template]p3
8534+
// [...] In this comparison, if P is unconstrained, the constraints on A
8535+
// are not considered.
8536+
if (ParamsAC.empty())
8537+
return false;
85468538

8547-
Template->getAssociatedConstraints(TemplateAC);
8539+
Template->getAssociatedConstraints(TemplateAC);
85488540

8549-
bool IsParamAtLeastAsConstrained;
8550-
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
8551-
IsParamAtLeastAsConstrained))
8552-
return true;
8553-
if (!IsParamAtLeastAsConstrained) {
8554-
Diag(Arg.getLocation(),
8555-
diag::err_template_template_parameter_not_at_least_as_constrained)
8556-
<< Template << Param << Arg.getSourceRange();
8557-
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
8558-
Diag(Template->getLocation(), diag::note_entity_declared_at)
8559-
<< Template;
8560-
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
8561-
TemplateAC);
8562-
return true;
8563-
}
8564-
return false;
8565-
}
8566-
// FIXME: Produce better diagnostics for deduction failures.
8541+
bool IsParamAtLeastAsConstrained;
8542+
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
8543+
IsParamAtLeastAsConstrained))
8544+
return true;
8545+
if (!IsParamAtLeastAsConstrained) {
8546+
Diag(Arg.getLocation(),
8547+
diag::err_template_template_parameter_not_at_least_as_constrained)
8548+
<< Template << Param << Arg.getSourceRange();
8549+
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
8550+
Diag(Template->getLocation(), diag::note_entity_declared_at) << Template;
8551+
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
8552+
TemplateAC);
8553+
return true;
85678554
}
8568-
8569-
return !TemplateParameterListsAreEqual(Template->getTemplateParameters(),
8570-
Params,
8571-
true,
8572-
TPL_TemplateTemplateArgumentMatch,
8573-
Arg.getLocation());
8555+
return false;
85748556
}
85758557

85768558
static Sema::SemaDiagnosticBuilder noteLocation(Sema &S, const NamedDecl &Decl,

0 commit comments

Comments
 (0)