Skip to content

Commit 7de7d24

Browse files
committed
[clang] CWG2398: improve overload resolution backwards compat
With this change, we discriminate if the primary template and which partial specializations would have participated in overload resolution prior to P0522 changes. We collect those in an initial set. If this set is not empty, or the primary template would have matched, we proceed with this set as the candidates for overload resolution. Otherwise, we build a new overload set with everything else, and proceed as usual.
1 parent 958703d commit 7de7d24

File tree

7 files changed

+95
-52
lines changed

7 files changed

+95
-52
lines changed

clang/include/clang/Sema/Sema.h

+9-5
Original file line numberDiff line numberDiff line change
@@ -11703,7 +11703,8 @@ class Sema final : public SemaBase {
1170311703
SourceLocation RAngleLoc, unsigned ArgumentPackIndex,
1170411704
SmallVectorImpl<TemplateArgument> &SugaredConverted,
1170511705
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
11706-
CheckTemplateArgumentKind CTAK);
11706+
CheckTemplateArgumentKind CTAK,
11707+
bool *MatchedPackOnParmToNonPackOnArg);
1170711708

1170811709
/// Check that the given template arguments can be provided to
1170911710
/// the given template, converting the arguments along the way.
@@ -11750,7 +11751,8 @@ class Sema final : public SemaBase {
1175011751
SmallVectorImpl<TemplateArgument> &SugaredConverted,
1175111752
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
1175211753
bool UpdateArgsWithConversions = true,
11753-
bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false);
11754+
bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false,
11755+
bool *MatchedPackOnParmToNonPackOnArg = nullptr);
1175411756

1175511757
bool CheckTemplateTypeArgument(
1175611758
TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg,
@@ -11784,7 +11786,8 @@ class Sema final : public SemaBase {
1178411786
/// It returns true if an error occurred, and false otherwise.
1178511787
bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
1178611788
TemplateParameterList *Params,
11787-
TemplateArgumentLoc &Arg, bool IsDeduced);
11789+
TemplateArgumentLoc &Arg, bool IsDeduced,
11790+
bool *MatchedPackOnParmToNonPackOnArg);
1178811791

1178911792
void NoteTemplateLocation(const NamedDecl &Decl,
1179011793
std::optional<SourceRange> ParamRange = {});
@@ -12485,7 +12488,7 @@ class Sema final : public SemaBase {
1248512488
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
1248612489
TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg,
1248712490
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
12488-
bool IsDeduced);
12491+
bool IsDeduced, bool *MatchedPackOnParmToNonPackOnArg);
1248912492

1249012493
/// Mark which template parameters are used in a given expression.
1249112494
///
@@ -13484,7 +13487,8 @@ class Sema final : public SemaBase {
1348413487
bool InstantiateClassTemplateSpecialization(
1348513488
SourceLocation PointOfInstantiation,
1348613489
ClassTemplateSpecializationDecl *ClassTemplateSpec,
13487-
TemplateSpecializationKind TSK, bool Complain = true);
13490+
TemplateSpecializationKind TSK, bool Complain = true,
13491+
bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false);
1348813492

1348913493
/// Instantiates the definitions of all of the member
1349013494
/// of the given class, which is an instantiation of a class template

clang/include/clang/Sema/TemplateDeduction.h

+13
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ class TemplateDeductionInfo {
5151
/// Have we suppressed an error during deduction?
5252
bool HasSFINAEDiagnostic = false;
5353

54+
/// Have we matched any packs on the parameter side, versus any non-packs on
55+
/// the argument side, in a context where the opposite matching is also
56+
/// allowed?
57+
bool MatchedPackOnParmToNonPackOnArg = false;
58+
5459
/// The template parameter depth for which we're performing deduction.
5560
unsigned DeducedDepth;
5661

@@ -87,6 +92,14 @@ class TemplateDeductionInfo {
8792
return DeducedDepth;
8893
}
8994

95+
bool hasMatchedPackOnParmToNonPackOnArg() const {
96+
return MatchedPackOnParmToNonPackOnArg;
97+
}
98+
99+
void setMatchedPackOnParmToNonPackOnArg() {
100+
MatchedPackOnParmToNonPackOnArg = true;
101+
}
102+
90103
/// Get the number of explicitly-specified arguments.
91104
unsigned getNumExplicitArgs() const {
92105
return ExplicitArgs;

clang/lib/Sema/SemaLookup.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -3661,7 +3661,8 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
36613661
TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit);
36623662
if (CheckTemplateArgument(
36633663
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
3664-
0, SugaredChecked, CanonicalChecked, CTAK_Specified) ||
3664+
0, SugaredChecked, CanonicalChecked, CTAK_Specified,
3665+
/*MatchedPackOnParmToNonPackOnArg=*/nullptr) ||
36653666
Trap.hasErrorOccurred())
36663667
IsTemplate = false;
36673668
}

clang/lib/Sema/SemaTemplate.cpp

+26-18
Original file line numberDiff line numberDiff line change
@@ -5018,7 +5018,7 @@ bool Sema::CheckTemplateArgument(
50185018
unsigned ArgumentPackIndex,
50195019
SmallVectorImpl<TemplateArgument> &SugaredConverted,
50205020
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
5021-
CheckTemplateArgumentKind CTAK) {
5021+
CheckTemplateArgumentKind CTAK, bool *MatchedPackOnParmToNonPackOnArg) {
50225022
// Check template type parameters.
50235023
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
50245024
return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted,
@@ -5234,7 +5234,8 @@ bool Sema::CheckTemplateArgument(
52345234
case TemplateArgument::Template:
52355235
case TemplateArgument::TemplateExpansion:
52365236
if (CheckTemplateTemplateArgument(TempParm, Params, Arg,
5237-
/*IsDeduced=*/CTAK != CTAK_Specified))
5237+
/*IsDeduced=*/CTAK != CTAK_Specified,
5238+
MatchedPackOnParmToNonPackOnArg))
52385239
return true;
52395240

52405241
SugaredConverted.push_back(Arg.getArgument());
@@ -5308,7 +5309,7 @@ bool Sema::CheckTemplateArgumentList(
53085309
SmallVectorImpl<TemplateArgument> &SugaredConverted,
53095310
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
53105311
bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied,
5311-
bool PartialOrderingTTP) {
5312+
bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) {
53125313

53135314
if (ConstraintsNotSatisfied)
53145315
*ConstraintsNotSatisfied = false;
@@ -5384,10 +5385,10 @@ bool Sema::CheckTemplateArgumentList(
53845385

53855386
if (ArgIdx < NumArgs) {
53865387
// Check the template argument we were given.
5387-
if (CheckTemplateArgument(*Param, NewArgs[ArgIdx], Template, TemplateLoc,
5388-
RAngleLoc, SugaredArgumentPack.size(),
5389-
SugaredConverted, CanonicalConverted,
5390-
CTAK_Specified))
5388+
if (CheckTemplateArgument(
5389+
*Param, NewArgs[ArgIdx], Template, TemplateLoc, RAngleLoc,
5390+
SugaredArgumentPack.size(), SugaredConverted, CanonicalConverted,
5391+
CTAK_Specified, MatchedPackOnParmToNonPackOnArg))
53915392
return true;
53925393

53935394
CanonicalConverted.back().setIsDefaulted(
@@ -5564,7 +5565,8 @@ bool Sema::CheckTemplateArgumentList(
55645565
// Check the default template argument.
55655566
if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, RAngleLoc, 0,
55665567
SugaredConverted, CanonicalConverted,
5567-
CTAK_Specified))
5568+
CTAK_Specified,
5569+
/*MatchedPackOnParmToNonPackOnArg=*/nullptr))
55685570
return true;
55695571

55705572
SugaredConverted.back().setIsDefaulted(true);
@@ -7147,10 +7149,10 @@ static void DiagnoseTemplateParameterListArityMismatch(
71477149
Sema &S, TemplateParameterList *New, TemplateParameterList *Old,
71487150
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc);
71497151

7150-
bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
7151-
TemplateParameterList *Params,
7152-
TemplateArgumentLoc &Arg,
7153-
bool IsDeduced) {
7152+
bool Sema::CheckTemplateTemplateArgument(
7153+
TemplateTemplateParmDecl *Param, TemplateParameterList *Params,
7154+
TemplateArgumentLoc &Arg, bool IsDeduced,
7155+
bool *MatchedPackOnParmToNonPackOnArg) {
71547156
TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
71557157
auto [Template, DefaultArgs] = Name.getTemplateDeclAndDefaultArgs();
71567158
if (!Template) {
@@ -7194,7 +7196,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
71947196
// A template-argument matches a template template-parameter P when P
71957197
// is at least as specialized as the template-argument A.
71967198
if (!isTemplateTemplateParameterAtLeastAsSpecializedAs(
7197-
Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced))
7199+
Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced,
7200+
MatchedPackOnParmToNonPackOnArg))
71987201
return true;
71997202
// P2113
72007203
// C++20[temp.func.order]p2
@@ -9611,11 +9614,14 @@ DeclResult Sema::ActOnExplicitInstantiation(
96119614

96129615
// Check that the template argument list is well-formed for this
96139616
// template.
9617+
bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false;
96149618
SmallVector<TemplateArgument, 4> SugaredConverted, CanonicalConverted;
9615-
if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, TemplateArgs,
9616-
/*DefaultArgs=*/{}, false, SugaredConverted,
9617-
CanonicalConverted,
9618-
/*UpdateArgsWithConversions=*/true))
9619+
if (CheckTemplateArgumentList(
9620+
ClassTemplate, TemplateNameLoc, TemplateArgs,
9621+
/*DefaultArgs=*/{}, false, SugaredConverted, CanonicalConverted,
9622+
/*UpdateArgsWithConversions=*/true,
9623+
/*ConstraintsNotSatisfied=*/nullptr, /*PartialOrderingTTP=*/false,
9624+
&PrimaryHasMatchedPackOnParmToNonPackOnArg))
96199625
return true;
96209626

96219627
// Find the class template specialization declaration that
@@ -9736,7 +9742,9 @@ DeclResult Sema::ActOnExplicitInstantiation(
97369742
= cast_or_null<ClassTemplateSpecializationDecl>(
97379743
Specialization->getDefinition());
97389744
if (!Def)
9739-
InstantiateClassTemplateSpecialization(TemplateNameLoc, Specialization, TSK);
9745+
InstantiateClassTemplateSpecialization(
9746+
TemplateNameLoc, Specialization, TSK,
9747+
/*Complain=*/true, PrimaryHasMatchedPackOnParmToNonPackOnArg);
97409748
else if (TSK == TSK_ExplicitInstantiationDefinition) {
97419749
MarkVTableUsed(TemplateNameLoc, Specialization, true);
97429750
Specialization->setPointOfInstantiation(Def->getPointOfInstantiation());

clang/lib/Sema/SemaTemplateDeduction.cpp

+30-13
Original file line numberDiff line numberDiff line change
@@ -2795,8 +2795,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
27952795
for (; hasTemplateArgumentForDeduction(As, ArgIdx) &&
27962796
PackScope.hasNextElement();
27972797
++ArgIdx) {
2798-
if (!FoldPackParameter && !As[ArgIdx].isPackExpansion())
2799-
return TemplateDeductionResult::MiscellaneousDeductionFailure;
2798+
if (!As[ArgIdx].isPackExpansion()) {
2799+
if (!FoldPackParameter)
2800+
return TemplateDeductionResult::MiscellaneousDeductionFailure;
2801+
if (FoldPackArgument)
2802+
Info.setMatchedPackOnParmToNonPackOnArg();
2803+
}
28002804
// Deduce template arguments from the pattern.
28012805
if (auto Result = DeduceTemplateArguments(
28022806
S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering,
@@ -2990,15 +2994,20 @@ static bool ConvertDeducedTemplateArgument(
29902994
TemplateArgumentLoc ArgLoc = S.getTrivialTemplateArgumentLoc(
29912995
Arg, QualType(), Info.getLocation(), Param);
29922996

2997+
bool MatchedPackOnParmToNonPackOnArg = false;
29932998
// Check the template argument, converting it as necessary.
2994-
return S.CheckTemplateArgument(
2999+
auto Res = S.CheckTemplateArgument(
29953000
Param, ArgLoc, Template, Template->getLocation(),
29963001
Template->getSourceRange().getEnd(), ArgumentPackIndex, SugaredOutput,
29973002
CanonicalOutput,
29983003
IsDeduced
29993004
? (Arg.wasDeducedFromArrayBound() ? Sema::CTAK_DeducedFromArrayBound
30003005
: Sema::CTAK_Deduced)
3001-
: Sema::CTAK_Specified);
3006+
: Sema::CTAK_Specified,
3007+
&MatchedPackOnParmToNonPackOnArg);
3008+
if (MatchedPackOnParmToNonPackOnArg)
3009+
Info.setMatchedPackOnParmToNonPackOnArg();
3010+
return Res;
30023011
};
30033012

30043013
if (Arg.getKind() == TemplateArgument::Pack) {
@@ -3193,7 +3202,8 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments(
31933202
// Check whether we can actually use the default argument.
31943203
if (S.CheckTemplateArgument(
31953204
Param, DefArg, TD, TD->getLocation(), TD->getSourceRange().getEnd(),
3196-
0, SugaredBuilder, CanonicalBuilder, Sema::CTAK_Specified)) {
3205+
0, SugaredBuilder, CanonicalBuilder, Sema::CTAK_Specified,
3206+
/*MatchedPackOnParmToNonPackOnArg=*/nullptr)) {
31973207
Info.Param = makeTemplateParameter(
31983208
const_cast<NamedDecl *>(TemplateParams->getParam(I)));
31993209
// FIXME: These template arguments are temporary. Free them!
@@ -3342,16 +3352,20 @@ FinishTemplateArgumentDeduction(
33423352
return TemplateDeductionResult::SubstitutionFailure;
33433353
}
33443354

3355+
bool MatchedPackOnParmToNonPackOnArg = false;
33453356
bool ConstraintsNotSatisfied;
33463357
SmallVector<TemplateArgument, 4> SugaredConvertedInstArgs,
33473358
CanonicalConvertedInstArgs;
33483359
if (S.CheckTemplateArgumentList(
33493360
Template, Partial->getLocation(), InstArgs, /*DefaultArgs=*/{}, false,
33503361
SugaredConvertedInstArgs, CanonicalConvertedInstArgs,
3351-
/*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied))
3362+
/*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied,
3363+
/*PartialOrderingTTP=*/false, &MatchedPackOnParmToNonPackOnArg))
33523364
return ConstraintsNotSatisfied
33533365
? TemplateDeductionResult::ConstraintsNotSatisfied
33543366
: TemplateDeductionResult::SubstitutionFailure;
3367+
if (MatchedPackOnParmToNonPackOnArg)
3368+
Info.setMatchedPackOnParmToNonPackOnArg();
33553369

33563370
TemplateParameterList *TemplateParams = Template->getTemplateParameters();
33573371
for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
@@ -6491,8 +6505,8 @@ bool Sema::isMoreSpecializedThanPrimary(
64916505

64926506
bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
64936507
TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg,
6494-
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
6495-
bool IsDeduced) {
6508+
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, bool IsDeduced,
6509+
bool *MatchedPackOnParmToNonPackOnArg) {
64966510
// C++1z [temp.arg.template]p4: (DR 150)
64976511
// A template template-parameter P is at least as specialized as a
64986512
// template template-argument A if, given the following rewrite to two
@@ -6544,11 +6558,11 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
65446558
// If the rewrite produces an invalid type, then P is not at least as
65456559
// specialized as A.
65466560
SmallVector<TemplateArgument, 4> CanonicalPArgs;
6547-
if (CheckTemplateArgumentList(AArg, ArgLoc, PArgList, DefaultArgs, false,
6548-
PArgs, CanonicalPArgs,
6549-
/*UpdateArgsWithConversions=*/true,
6550-
/*ConstraintsNotSatisfied=*/nullptr,
6551-
/*PartialOrderingTTP=*/true))
6561+
if (CheckTemplateArgumentList(
6562+
AArg, ArgLoc, PArgList, DefaultArgs, false, PArgs, CanonicalPArgs,
6563+
/*UpdateArgsWithConversions=*/true,
6564+
/*ConstraintsNotSatisfied=*/nullptr,
6565+
/*PartialOrderingTTP=*/true, MatchedPackOnParmToNonPackOnArg))
65526566
return false;
65536567
}
65546568

@@ -6574,6 +6588,9 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
65746588
IsDeduced ? PackFold::ArgumentToParameter : PackFold::Both,
65756589
/*HasDeducedAnyParam=*/nullptr)) {
65766590
case clang::TemplateDeductionResult::Success:
6591+
if (MatchedPackOnParmToNonPackOnArg &&
6592+
Info.hasMatchedPackOnParmToNonPackOnArg())
6593+
*MatchedPackOnParmToNonPackOnArg = true;
65776594
break;
65786595

65796596
case TemplateDeductionResult::MiscellaneousDeductionFailure:

clang/lib/Sema/SemaTemplateInstantiate.cpp

+14-10
Original file line numberDiff line numberDiff line change
@@ -3841,11 +3841,11 @@ bool Sema::usesPartialOrExplicitSpecialization(
38413841
/// Get the instantiation pattern to use to instantiate the definition of a
38423842
/// given ClassTemplateSpecializationDecl (either the pattern of the primary
38433843
/// template or of a partial specialization).
3844-
static ActionResult<CXXRecordDecl *>
3845-
getPatternForClassTemplateSpecialization(
3844+
static ActionResult<CXXRecordDecl *> getPatternForClassTemplateSpecialization(
38463845
Sema &S, SourceLocation PointOfInstantiation,
38473846
ClassTemplateSpecializationDecl *ClassTemplateSpec,
3848-
TemplateSpecializationKind TSK) {
3847+
TemplateSpecializationKind TSK,
3848+
bool PrimaryHasMatchedPackOnParmToNonPackOnArg) {
38493849
Sema::InstantiatingTemplate Inst(S, PointOfInstantiation, ClassTemplateSpec);
38503850
if (Inst.isInvalid())
38513851
return {/*Invalid=*/true};
@@ -3868,7 +3868,7 @@ getPatternForClassTemplateSpecialization(
38683868
// specialization with the template argument lists of the partial
38693869
// specializations.
38703870
typedef PartialSpecMatchResult MatchResult;
3871-
SmallVector<MatchResult, 4> Matched;
3871+
SmallVector<MatchResult, 4> Matched, ExtraMatched;
38723872
SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
38733873
Template->getPartialSpecializations(PartialSpecs);
38743874
TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation);
@@ -3885,11 +3885,13 @@ getPatternForClassTemplateSpecialization(
38853885
MakeDeductionFailureInfo(S.Context, Result, Info));
38863886
(void)Result;
38873887
} else {
3888-
Matched.push_back(PartialSpecMatchResult());
3889-
Matched.back().Partial = Partial;
3890-
Matched.back().Args = Info.takeCanonical();
3888+
auto &List =
3889+
Info.hasMatchedPackOnParmToNonPackOnArg() ? ExtraMatched : Matched;
3890+
List.push_back(MatchResult{Partial, Info.takeCanonical()});
38913891
}
38923892
}
3893+
if (Matched.empty() && PrimaryHasMatchedPackOnParmToNonPackOnArg)
3894+
Matched = std::move(ExtraMatched);
38933895

38943896
// If we're dealing with a member template where the template parameters
38953897
// have been instantiated, this provides the original template parameters
@@ -3992,16 +3994,18 @@ getPatternForClassTemplateSpecialization(
39923994
bool Sema::InstantiateClassTemplateSpecialization(
39933995
SourceLocation PointOfInstantiation,
39943996
ClassTemplateSpecializationDecl *ClassTemplateSpec,
3995-
TemplateSpecializationKind TSK, bool Complain) {
3997+
TemplateSpecializationKind TSK, bool Complain,
3998+
bool PrimaryHasMatchedPackOnParmToNonPackOnArg) {
39963999
// Perform the actual instantiation on the canonical declaration.
39974000
ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
39984001
ClassTemplateSpec->getCanonicalDecl());
39994002
if (ClassTemplateSpec->isInvalidDecl())
40004003
return true;
40014004

40024005
ActionResult<CXXRecordDecl *> Pattern =
4003-
getPatternForClassTemplateSpecialization(*this, PointOfInstantiation,
4004-
ClassTemplateSpec, TSK);
4006+
getPatternForClassTemplateSpecialization(
4007+
*this, PointOfInstantiation, ClassTemplateSpec, TSK,
4008+
PrimaryHasMatchedPackOnParmToNonPackOnArg);
40054009
if (!Pattern.isUsable())
40064010
return Pattern.isInvalid();
40074011

clang/test/SemaTemplate/cwg2398.cpp

+1-5
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,14 @@ namespace ttp_defaults {
156156
namespace ttp_only {
157157
template <template <class... > class TT1> struct A { static constexpr int V = 0; };
158158
template <template <class > class TT2> struct A<TT2> { static constexpr int V = 1; };
159-
// new-note@-1 {{partial specialization matches}}
160159
template <template <class, class> class TT3> struct A<TT3> { static constexpr int V = 2; };
161-
// new-note@-1 {{partial specialization matches}}
162160

163161
template <class ... > struct B;
164162
template <class > struct C;
165163
template <class, class > struct D;
166164
template <class, class, class> struct E;
167165

168-
static_assert(A<B>::V == 0); // new-error {{ambiguous partial specializations}}
166+
static_assert(A<B>::V == 0);
169167
static_assert(A<C>::V == 1);
170168
static_assert(A<D>::V == 2);
171169
static_assert(A<E>::V == 0);
@@ -412,11 +410,9 @@ namespace partial {
412410
template<template<class... T1s> class TT1> struct A {};
413411

414412
template<template<class T2> class TT2> struct A<TT2>;
415-
// new-note@-1 {{template is declared here}}
416413

417414
template<class... T3s> struct B;
418415
template struct A<B>;
419-
// new-error@-1 {{explicit instantiation of undefined template}}
420416
} // namespace t1
421417
namespace t2 {
422418
template<template<class... T1s> class TT1> struct A;

0 commit comments

Comments
 (0)