Skip to content

Commit 6213aa5

Browse files
authored
Reland: [clang] Finish implementation of P0522 (#111711)
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 8d35ab8 commit 6213aa5

File tree

17 files changed

+652
-266
lines changed

17 files changed

+652
-266
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ C++23 Feature Support
174174
C++20 Feature Support
175175
^^^^^^^^^^^^^^^^^^^^^
176176

177+
C++17 Feature Support
178+
^^^^^^^^^^^^^^^^^^^^^
179+
- The implementation of the relaxed template template argument matching rules is
180+
more complete and reliable, and should provide more accurate diagnostics.
177181

178182
Resolutions to C++ Defect Reports
179183
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -331,6 +335,10 @@ Improvements to Clang's diagnostics
331335

332336
- Clang now diagnoses when the result of a [[nodiscard]] function is discarded after being cast in C. Fixes #GH104391.
333337

338+
- Clang now properly explains the reason a template template argument failed to
339+
match a template template parameter, in terms of the C++17 relaxed matching rules
340+
instead of the old ones.
341+
334342
- Don't emit duplicated dangling diagnostics. (#GH93386).
335343

336344
- Improved diagnostic when trying to befriend a concept. (#GH45182).
@@ -440,6 +448,8 @@ Bug Fixes to C++ Support
440448
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
441449
- When performing partial ordering of function templates, clang now checks that
442450
the deduction was consistent. Fixes (#GH18291).
451+
- Fixes to several issues in partial ordering of template template parameters, which
452+
were documented in the test suite.
443453
- Fixed an assertion failure about a constraint of a friend function template references to a value with greater
444454
template depth than the friend function template. (#GH98258)
445455
- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5262,6 +5262,13 @@ def note_template_arg_refers_here_func : Note<
52625262
def err_template_arg_template_params_mismatch : Error<
52635263
"template template argument has different template parameters than its "
52645264
"corresponding template template parameter">;
5265+
def note_template_arg_template_params_mismatch : Note<
5266+
"template template argument has different template parameters than its "
5267+
"corresponding template template parameter">;
5268+
def err_non_deduced_mismatch : Error<
5269+
"could not match %diff{$ against $|types}0,1">;
5270+
def err_inconsistent_deduction : Error<
5271+
"conflicting deduction %diff{$ against $|types}0,1 for parameter">;
52655272
def err_template_arg_not_integral_or_enumeral : Error<
52665273
"non-type template argument of type %0 must have an integral or enumeration"
52675274
" type">;

clang/include/clang/Sema/Sema.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12417,8 +12417,9 @@ class Sema final : public SemaBase {
1241712417
sema::TemplateDeductionInfo &Info);
1241812418

1241912419
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
12420-
TemplateParameterList *PParam, TemplateDecl *AArg,
12421-
const DefaultArguments &DefaultArgs, SourceLocation Loc, bool IsDeduced);
12420+
TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg,
12421+
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
12422+
bool IsDeduced);
1242212423

1242312424
/// Mark which template parameters are used in a given expression.
1242412425
///
@@ -12727,6 +12728,9 @@ class Sema final : public SemaBase {
1272712728

1272812729
/// We are instantiating a type alias template declaration.
1272912730
TypeAliasTemplateInstantiation,
12731+
12732+
/// We are performing partial ordering for template template parameters.
12733+
PartialOrderingTTP,
1273012734
} Kind;
1273112735

1273212736
/// Was the enclosing context a non-instantiation SFINAE context?
@@ -12948,6 +12952,12 @@ class Sema final : public SemaBase {
1294812952
TemplateDecl *Entity, BuildingDeductionGuidesTag,
1294912953
SourceRange InstantiationRange = SourceRange());
1295012954

12955+
struct PartialOrderingTTP {};
12956+
/// \brief Note that we are partial ordering template template parameters.
12957+
InstantiatingTemplate(Sema &SemaRef, SourceLocation ArgLoc,
12958+
PartialOrderingTTP, TemplateDecl *PArg,
12959+
SourceRange InstantiationRange = SourceRange());
12960+
1295112961
/// Note that we have finished instantiating this template.
1295212962
void Clear();
1295312963

clang/lib/Frontend/FrontendActions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
457457
return "BuildingDeductionGuides";
458458
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
459459
return "TypeAliasTemplateInstantiation";
460+
case CodeSynthesisContext::PartialOrderingTTP:
461+
return "PartialOrderingTTP";
460462
}
461463
return "";
462464
}

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 38 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5502,8 +5502,7 @@ bool Sema::CheckTemplateArgumentList(
55025502
DefaultArgs && ParamIdx >= DefaultArgs.StartPos) {
55035503
// All written arguments should have been consumed by this point.
55045504
assert(ArgIdx == NumArgs && "bad default argument deduction");
5505-
// FIXME: Don't ignore parameter packs.
5506-
if (ParamIdx == DefaultArgs.StartPos && !(*Param)->isParameterPack()) {
5505+
if (ParamIdx == DefaultArgs.StartPos) {
55075506
assert(Param + DefaultArgs.Args.size() <= ParamEnd);
55085507
// Default arguments from a DeducedTemplateName are already converted.
55095508
for (const TemplateArgument &DefArg : DefaultArgs.Args) {
@@ -5728,8 +5727,9 @@ bool Sema::CheckTemplateArgumentList(
57285727
// pack expansions; they might be empty. This can happen even if
57295728
// PartialTemplateArgs is false (the list of arguments is complete but
57305729
// still dependent).
5731-
if (ArgIdx < NumArgs && CurrentInstantiationScope &&
5732-
CurrentInstantiationScope->getPartiallySubstitutedPack()) {
5730+
if (PartialOrderingTTP ||
5731+
(CurrentInstantiationScope &&
5732+
CurrentInstantiationScope->getPartiallySubstitutedPack())) {
57335733
while (ArgIdx < NumArgs &&
57345734
NewArgs[ArgIdx].getArgument().isPackExpansion()) {
57355735
const TemplateArgument &Arg = NewArgs[ArgIdx++].getArgument();
@@ -7327,64 +7327,46 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
73277327
<< Template;
73287328
}
73297329

7330+
if (!getLangOpts().RelaxedTemplateTemplateArgs)
7331+
return !TemplateParameterListsAreEqual(
7332+
Template->getTemplateParameters(), Params, /*Complain=*/true,
7333+
TPL_TemplateTemplateArgumentMatch, Arg.getLocation());
7334+
73307335
// C++1z [temp.arg.template]p3: (DR 150)
73317336
// A template-argument matches a template template-parameter P when P
73327337
// is at least as specialized as the template-argument A.
7333-
if (getLangOpts().RelaxedTemplateTemplateArgs) {
7334-
// Quick check for the common case:
7335-
// If P contains a parameter pack, then A [...] matches P if each of A's
7336-
// template parameters matches the corresponding template parameter in
7337-
// the template-parameter-list of P.
7338-
if (TemplateParameterListsAreEqual(
7339-
Template->getTemplateParameters(), Params, false,
7340-
TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) &&
7341-
// If the argument has no associated constraints, then the parameter is
7342-
// definitely at least as specialized as the argument.
7343-
// Otherwise - we need a more thorough check.
7344-
!Template->hasAssociatedConstraints())
7345-
return false;
7346-
7347-
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(
7348-
Params, Template, DefaultArgs, Arg.getLocation(), IsDeduced)) {
7349-
// P2113
7350-
// C++20[temp.func.order]p2
7351-
// [...] If both deductions succeed, the partial ordering selects the
7352-
// more constrained template (if one exists) as determined below.
7353-
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
7354-
Params->getAssociatedConstraints(ParamsAC);
7355-
// C++2a[temp.arg.template]p3
7356-
// [...] In this comparison, if P is unconstrained, the constraints on A
7357-
// are not considered.
7358-
if (ParamsAC.empty())
7359-
return false;
7338+
if (!isTemplateTemplateParameterAtLeastAsSpecializedAs(
7339+
Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced))
7340+
return true;
7341+
// P2113
7342+
// C++20[temp.func.order]p2
7343+
// [...] If both deductions succeed, the partial ordering selects the
7344+
// more constrained template (if one exists) as determined below.
7345+
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
7346+
Params->getAssociatedConstraints(ParamsAC);
7347+
// C++20[temp.arg.template]p3
7348+
// [...] In this comparison, if P is unconstrained, the constraints on A
7349+
// are not considered.
7350+
if (ParamsAC.empty())
7351+
return false;
73607352

7361-
Template->getAssociatedConstraints(TemplateAC);
7353+
Template->getAssociatedConstraints(TemplateAC);
73627354

7363-
bool IsParamAtLeastAsConstrained;
7364-
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
7365-
IsParamAtLeastAsConstrained))
7366-
return true;
7367-
if (!IsParamAtLeastAsConstrained) {
7368-
Diag(Arg.getLocation(),
7369-
diag::err_template_template_parameter_not_at_least_as_constrained)
7370-
<< Template << Param << Arg.getSourceRange();
7371-
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
7372-
Diag(Template->getLocation(), diag::note_entity_declared_at)
7373-
<< Template;
7374-
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
7375-
TemplateAC);
7376-
return true;
7377-
}
7378-
return false;
7379-
}
7380-
// FIXME: Produce better diagnostics for deduction failures.
7355+
bool IsParamAtLeastAsConstrained;
7356+
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
7357+
IsParamAtLeastAsConstrained))
7358+
return true;
7359+
if (!IsParamAtLeastAsConstrained) {
7360+
Diag(Arg.getLocation(),
7361+
diag::err_template_template_parameter_not_at_least_as_constrained)
7362+
<< Template << Param << Arg.getSourceRange();
7363+
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
7364+
Diag(Template->getLocation(), diag::note_entity_declared_at) << Template;
7365+
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
7366+
TemplateAC);
7367+
return true;
73817368
}
7382-
7383-
return !TemplateParameterListsAreEqual(Template->getTemplateParameters(),
7384-
Params,
7385-
true,
7386-
TPL_TemplateTemplateArgumentMatch,
7387-
Arg.getLocation());
7369+
return false;
73887370
}
73897371

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

0 commit comments

Comments
 (0)