[llvm-branch-commits] [clang] [clang] CWG2398: improve overload resolution backwards compat (PR #107350)
Matheus Izvekov via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Sep 4 21:24:55 PDT 2024
https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/107350
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.
>From 7de7d24ddbd2a932f045c1a4455126388978d21c Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Thu, 5 Sep 2024 00:25:40 -0300
Subject: [PATCH] [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.
---
clang/include/clang/Sema/Sema.h | 14 ++++---
clang/include/clang/Sema/TemplateDeduction.h | 13 ++++++
clang/lib/Sema/SemaLookup.cpp | 3 +-
clang/lib/Sema/SemaTemplate.cpp | 44 ++++++++++++--------
clang/lib/Sema/SemaTemplateDeduction.cpp | 43 +++++++++++++------
clang/lib/Sema/SemaTemplateInstantiate.cpp | 24 ++++++-----
clang/test/SemaTemplate/cwg2398.cpp | 6 +--
7 files changed, 95 insertions(+), 52 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f11d6bc6f865a8..0338641d6c461e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11703,7 +11703,8 @@ class Sema final : public SemaBase {
SourceLocation RAngleLoc, unsigned ArgumentPackIndex,
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
- CheckTemplateArgumentKind CTAK);
+ CheckTemplateArgumentKind CTAK,
+ bool *MatchedPackOnParmToNonPackOnArg);
/// Check that the given template arguments can be provided to
/// the given template, converting the arguments along the way.
@@ -11750,7 +11751,8 @@ class Sema final : public SemaBase {
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
bool UpdateArgsWithConversions = true,
- bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false);
+ bool *ConstraintsNotSatisfied = nullptr, bool PartialOrderingTTP = false,
+ bool *MatchedPackOnParmToNonPackOnArg = nullptr);
bool CheckTemplateTypeArgument(
TemplateTypeParmDecl *Param, TemplateArgumentLoc &Arg,
@@ -11784,7 +11786,8 @@ class Sema final : public SemaBase {
/// It returns true if an error occurred, and false otherwise.
bool CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
TemplateParameterList *Params,
- TemplateArgumentLoc &Arg, bool IsDeduced);
+ TemplateArgumentLoc &Arg, bool IsDeduced,
+ bool *MatchedPackOnParmToNonPackOnArg);
void NoteTemplateLocation(const NamedDecl &Decl,
std::optional<SourceRange> ParamRange = {});
@@ -12485,7 +12488,7 @@ class Sema final : public SemaBase {
bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
TemplateParameterList *PParam, TemplateDecl *PArg, TemplateDecl *AArg,
const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
- bool IsDeduced);
+ bool IsDeduced, bool *MatchedPackOnParmToNonPackOnArg);
/// Mark which template parameters are used in a given expression.
///
@@ -13484,7 +13487,8 @@ class Sema final : public SemaBase {
bool InstantiateClassTemplateSpecialization(
SourceLocation PointOfInstantiation,
ClassTemplateSpecializationDecl *ClassTemplateSpec,
- TemplateSpecializationKind TSK, bool Complain = true);
+ TemplateSpecializationKind TSK, bool Complain = true,
+ bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false);
/// Instantiates the definitions of all of the member
/// of the given class, which is an instantiation of a class template
diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h
index 28b014fd84e4b3..9c12eef5c42a06 100644
--- a/clang/include/clang/Sema/TemplateDeduction.h
+++ b/clang/include/clang/Sema/TemplateDeduction.h
@@ -51,6 +51,11 @@ class TemplateDeductionInfo {
/// Have we suppressed an error during deduction?
bool HasSFINAEDiagnostic = false;
+ /// Have we matched any packs on the parameter side, versus any non-packs on
+ /// the argument side, in a context where the opposite matching is also
+ /// allowed?
+ bool MatchedPackOnParmToNonPackOnArg = false;
+
/// The template parameter depth for which we're performing deduction.
unsigned DeducedDepth;
@@ -87,6 +92,14 @@ class TemplateDeductionInfo {
return DeducedDepth;
}
+ bool hasMatchedPackOnParmToNonPackOnArg() const {
+ return MatchedPackOnParmToNonPackOnArg;
+ }
+
+ void setMatchedPackOnParmToNonPackOnArg() {
+ MatchedPackOnParmToNonPackOnArg = true;
+ }
+
/// Get the number of explicitly-specified arguments.
unsigned getNumExplicitArgs() const {
return ExplicitArgs;
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index d3d4bf27ae7283..b13af430e2de1c 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -3661,7 +3661,8 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
TemplateArgumentLoc Arg(TemplateArgument(StringLit), StringLit);
if (CheckTemplateArgument(
Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
- 0, SugaredChecked, CanonicalChecked, CTAK_Specified) ||
+ 0, SugaredChecked, CanonicalChecked, CTAK_Specified,
+ /*MatchedPackOnParmToNonPackOnArg=*/nullptr) ||
Trap.hasErrorOccurred())
IsTemplate = false;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 74fcd85843ad7d..6330a7ca69757f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5018,7 +5018,7 @@ bool Sema::CheckTemplateArgument(
unsigned ArgumentPackIndex,
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
- CheckTemplateArgumentKind CTAK) {
+ CheckTemplateArgumentKind CTAK, bool *MatchedPackOnParmToNonPackOnArg) {
// Check template type parameters.
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param))
return CheckTemplateTypeArgument(TTP, Arg, SugaredConverted,
@@ -5234,7 +5234,8 @@ bool Sema::CheckTemplateArgument(
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
if (CheckTemplateTemplateArgument(TempParm, Params, Arg,
- /*IsDeduced=*/CTAK != CTAK_Specified))
+ /*IsDeduced=*/CTAK != CTAK_Specified,
+ MatchedPackOnParmToNonPackOnArg))
return true;
SugaredConverted.push_back(Arg.getArgument());
@@ -5308,7 +5309,7 @@ bool Sema::CheckTemplateArgumentList(
SmallVectorImpl<TemplateArgument> &SugaredConverted,
SmallVectorImpl<TemplateArgument> &CanonicalConverted,
bool UpdateArgsWithConversions, bool *ConstraintsNotSatisfied,
- bool PartialOrderingTTP) {
+ bool PartialOrderingTTP, bool *MatchedPackOnParmToNonPackOnArg) {
if (ConstraintsNotSatisfied)
*ConstraintsNotSatisfied = false;
@@ -5384,10 +5385,10 @@ bool Sema::CheckTemplateArgumentList(
if (ArgIdx < NumArgs) {
// Check the template argument we were given.
- if (CheckTemplateArgument(*Param, NewArgs[ArgIdx], Template, TemplateLoc,
- RAngleLoc, SugaredArgumentPack.size(),
- SugaredConverted, CanonicalConverted,
- CTAK_Specified))
+ if (CheckTemplateArgument(
+ *Param, NewArgs[ArgIdx], Template, TemplateLoc, RAngleLoc,
+ SugaredArgumentPack.size(), SugaredConverted, CanonicalConverted,
+ CTAK_Specified, MatchedPackOnParmToNonPackOnArg))
return true;
CanonicalConverted.back().setIsDefaulted(
@@ -5564,7 +5565,8 @@ bool Sema::CheckTemplateArgumentList(
// Check the default template argument.
if (CheckTemplateArgument(*Param, Arg, Template, TemplateLoc, RAngleLoc, 0,
SugaredConverted, CanonicalConverted,
- CTAK_Specified))
+ CTAK_Specified,
+ /*MatchedPackOnParmToNonPackOnArg=*/nullptr))
return true;
SugaredConverted.back().setIsDefaulted(true);
@@ -7147,10 +7149,10 @@ static void DiagnoseTemplateParameterListArityMismatch(
Sema &S, TemplateParameterList *New, TemplateParameterList *Old,
Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc);
-bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
- TemplateParameterList *Params,
- TemplateArgumentLoc &Arg,
- bool IsDeduced) {
+bool Sema::CheckTemplateTemplateArgument(
+ TemplateTemplateParmDecl *Param, TemplateParameterList *Params,
+ TemplateArgumentLoc &Arg, bool IsDeduced,
+ bool *MatchedPackOnParmToNonPackOnArg) {
TemplateName Name = Arg.getArgument().getAsTemplateOrTemplatePattern();
auto [Template, DefaultArgs] = Name.getTemplateDeclAndDefaultArgs();
if (!Template) {
@@ -7194,7 +7196,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
// A template-argument matches a template template-parameter P when P
// is at least as specialized as the template-argument A.
if (!isTemplateTemplateParameterAtLeastAsSpecializedAs(
- Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced))
+ Params, Param, Template, DefaultArgs, Arg.getLocation(), IsDeduced,
+ MatchedPackOnParmToNonPackOnArg))
return true;
// P2113
// C++20[temp.func.order]p2
@@ -9611,11 +9614,14 @@ DeclResult Sema::ActOnExplicitInstantiation(
// Check that the template argument list is well-formed for this
// template.
+ bool PrimaryHasMatchedPackOnParmToNonPackOnArg = false;
SmallVector<TemplateArgument, 4> SugaredConverted, CanonicalConverted;
- if (CheckTemplateArgumentList(ClassTemplate, TemplateNameLoc, TemplateArgs,
- /*DefaultArgs=*/{}, false, SugaredConverted,
- CanonicalConverted,
- /*UpdateArgsWithConversions=*/true))
+ if (CheckTemplateArgumentList(
+ ClassTemplate, TemplateNameLoc, TemplateArgs,
+ /*DefaultArgs=*/{}, false, SugaredConverted, CanonicalConverted,
+ /*UpdateArgsWithConversions=*/true,
+ /*ConstraintsNotSatisfied=*/nullptr, /*PartialOrderingTTP=*/false,
+ &PrimaryHasMatchedPackOnParmToNonPackOnArg))
return true;
// Find the class template specialization declaration that
@@ -9736,7 +9742,9 @@ DeclResult Sema::ActOnExplicitInstantiation(
= cast_or_null<ClassTemplateSpecializationDecl>(
Specialization->getDefinition());
if (!Def)
- InstantiateClassTemplateSpecialization(TemplateNameLoc, Specialization, TSK);
+ InstantiateClassTemplateSpecialization(
+ TemplateNameLoc, Specialization, TSK,
+ /*Complain=*/true, PrimaryHasMatchedPackOnParmToNonPackOnArg);
else if (TSK == TSK_ExplicitInstantiationDefinition) {
MarkVTableUsed(TemplateNameLoc, Specialization, true);
Specialization->setPointOfInstantiation(Def->getPointOfInstantiation());
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index b6381245983a9f..12b4b8ced3c1f2 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2795,8 +2795,12 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
for (; hasTemplateArgumentForDeduction(As, ArgIdx) &&
PackScope.hasNextElement();
++ArgIdx) {
- if (!FoldPackParameter && !As[ArgIdx].isPackExpansion())
- return TemplateDeductionResult::MiscellaneousDeductionFailure;
+ if (!As[ArgIdx].isPackExpansion()) {
+ if (!FoldPackParameter)
+ return TemplateDeductionResult::MiscellaneousDeductionFailure;
+ if (FoldPackArgument)
+ Info.setMatchedPackOnParmToNonPackOnArg();
+ }
// Deduce template arguments from the pattern.
if (auto Result = DeduceTemplateArguments(
S, TemplateParams, Pattern, As[ArgIdx], Info, PartialOrdering,
@@ -2990,15 +2994,20 @@ static bool ConvertDeducedTemplateArgument(
TemplateArgumentLoc ArgLoc = S.getTrivialTemplateArgumentLoc(
Arg, QualType(), Info.getLocation(), Param);
+ bool MatchedPackOnParmToNonPackOnArg = false;
// Check the template argument, converting it as necessary.
- return S.CheckTemplateArgument(
+ auto Res = S.CheckTemplateArgument(
Param, ArgLoc, Template, Template->getLocation(),
Template->getSourceRange().getEnd(), ArgumentPackIndex, SugaredOutput,
CanonicalOutput,
IsDeduced
? (Arg.wasDeducedFromArrayBound() ? Sema::CTAK_DeducedFromArrayBound
: Sema::CTAK_Deduced)
- : Sema::CTAK_Specified);
+ : Sema::CTAK_Specified,
+ &MatchedPackOnParmToNonPackOnArg);
+ if (MatchedPackOnParmToNonPackOnArg)
+ Info.setMatchedPackOnParmToNonPackOnArg();
+ return Res;
};
if (Arg.getKind() == TemplateArgument::Pack) {
@@ -3193,7 +3202,8 @@ static TemplateDeductionResult ConvertDeducedTemplateArguments(
// Check whether we can actually use the default argument.
if (S.CheckTemplateArgument(
Param, DefArg, TD, TD->getLocation(), TD->getSourceRange().getEnd(),
- 0, SugaredBuilder, CanonicalBuilder, Sema::CTAK_Specified)) {
+ 0, SugaredBuilder, CanonicalBuilder, Sema::CTAK_Specified,
+ /*MatchedPackOnParmToNonPackOnArg=*/nullptr)) {
Info.Param = makeTemplateParameter(
const_cast<NamedDecl *>(TemplateParams->getParam(I)));
// FIXME: These template arguments are temporary. Free them!
@@ -3342,16 +3352,20 @@ FinishTemplateArgumentDeduction(
return TemplateDeductionResult::SubstitutionFailure;
}
+ bool MatchedPackOnParmToNonPackOnArg = false;
bool ConstraintsNotSatisfied;
SmallVector<TemplateArgument, 4> SugaredConvertedInstArgs,
CanonicalConvertedInstArgs;
if (S.CheckTemplateArgumentList(
Template, Partial->getLocation(), InstArgs, /*DefaultArgs=*/{}, false,
SugaredConvertedInstArgs, CanonicalConvertedInstArgs,
- /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied))
+ /*UpdateArgsWithConversions=*/true, &ConstraintsNotSatisfied,
+ /*PartialOrderingTTP=*/false, &MatchedPackOnParmToNonPackOnArg))
return ConstraintsNotSatisfied
? TemplateDeductionResult::ConstraintsNotSatisfied
: TemplateDeductionResult::SubstitutionFailure;
+ if (MatchedPackOnParmToNonPackOnArg)
+ Info.setMatchedPackOnParmToNonPackOnArg();
TemplateParameterList *TemplateParams = Template->getTemplateParameters();
for (unsigned I = 0, E = TemplateParams->size(); I != E; ++I) {
@@ -6491,8 +6505,8 @@ bool Sema::isMoreSpecializedThanPrimary(
bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
TemplateParameterList *P, TemplateDecl *PArg, TemplateDecl *AArg,
- const DefaultArguments &DefaultArgs, SourceLocation ArgLoc,
- bool IsDeduced) {
+ const DefaultArguments &DefaultArgs, SourceLocation ArgLoc, bool IsDeduced,
+ bool *MatchedPackOnParmToNonPackOnArg) {
// C++1z [temp.arg.template]p4: (DR 150)
// A template template-parameter P is at least as specialized as a
// template template-argument A if, given the following rewrite to two
@@ -6544,11 +6558,11 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
// If the rewrite produces an invalid type, then P is not at least as
// specialized as A.
SmallVector<TemplateArgument, 4> CanonicalPArgs;
- if (CheckTemplateArgumentList(AArg, ArgLoc, PArgList, DefaultArgs, false,
- PArgs, CanonicalPArgs,
- /*UpdateArgsWithConversions=*/true,
- /*ConstraintsNotSatisfied=*/nullptr,
- /*PartialOrderingTTP=*/true))
+ if (CheckTemplateArgumentList(
+ AArg, ArgLoc, PArgList, DefaultArgs, false, PArgs, CanonicalPArgs,
+ /*UpdateArgsWithConversions=*/true,
+ /*ConstraintsNotSatisfied=*/nullptr,
+ /*PartialOrderingTTP=*/true, MatchedPackOnParmToNonPackOnArg))
return false;
}
@@ -6574,6 +6588,9 @@ bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
IsDeduced ? PackFold::ArgumentToParameter : PackFold::Both,
/*HasDeducedAnyParam=*/nullptr)) {
case clang::TemplateDeductionResult::Success:
+ if (MatchedPackOnParmToNonPackOnArg &&
+ Info.hasMatchedPackOnParmToNonPackOnArg())
+ *MatchedPackOnParmToNonPackOnArg = true;
break;
case TemplateDeductionResult::MiscellaneousDeductionFailure:
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 4779dc327dbb0f..f7ac44909687d2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3841,11 +3841,11 @@ bool Sema::usesPartialOrExplicitSpecialization(
/// Get the instantiation pattern to use to instantiate the definition of a
/// given ClassTemplateSpecializationDecl (either the pattern of the primary
/// template or of a partial specialization).
-static ActionResult<CXXRecordDecl *>
-getPatternForClassTemplateSpecialization(
+static ActionResult<CXXRecordDecl *> getPatternForClassTemplateSpecialization(
Sema &S, SourceLocation PointOfInstantiation,
ClassTemplateSpecializationDecl *ClassTemplateSpec,
- TemplateSpecializationKind TSK) {
+ TemplateSpecializationKind TSK,
+ bool PrimaryHasMatchedPackOnParmToNonPackOnArg) {
Sema::InstantiatingTemplate Inst(S, PointOfInstantiation, ClassTemplateSpec);
if (Inst.isInvalid())
return {/*Invalid=*/true};
@@ -3868,7 +3868,7 @@ getPatternForClassTemplateSpecialization(
// specialization with the template argument lists of the partial
// specializations.
typedef PartialSpecMatchResult MatchResult;
- SmallVector<MatchResult, 4> Matched;
+ SmallVector<MatchResult, 4> Matched, ExtraMatched;
SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
Template->getPartialSpecializations(PartialSpecs);
TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation);
@@ -3885,11 +3885,13 @@ getPatternForClassTemplateSpecialization(
MakeDeductionFailureInfo(S.Context, Result, Info));
(void)Result;
} else {
- Matched.push_back(PartialSpecMatchResult());
- Matched.back().Partial = Partial;
- Matched.back().Args = Info.takeCanonical();
+ auto &List =
+ Info.hasMatchedPackOnParmToNonPackOnArg() ? ExtraMatched : Matched;
+ List.push_back(MatchResult{Partial, Info.takeCanonical()});
}
}
+ if (Matched.empty() && PrimaryHasMatchedPackOnParmToNonPackOnArg)
+ Matched = std::move(ExtraMatched);
// If we're dealing with a member template where the template parameters
// have been instantiated, this provides the original template parameters
@@ -3992,7 +3994,8 @@ getPatternForClassTemplateSpecialization(
bool Sema::InstantiateClassTemplateSpecialization(
SourceLocation PointOfInstantiation,
ClassTemplateSpecializationDecl *ClassTemplateSpec,
- TemplateSpecializationKind TSK, bool Complain) {
+ TemplateSpecializationKind TSK, bool Complain,
+ bool PrimaryHasMatchedPackOnParmToNonPackOnArg) {
// Perform the actual instantiation on the canonical declaration.
ClassTemplateSpec = cast<ClassTemplateSpecializationDecl>(
ClassTemplateSpec->getCanonicalDecl());
@@ -4000,8 +4003,9 @@ bool Sema::InstantiateClassTemplateSpecialization(
return true;
ActionResult<CXXRecordDecl *> Pattern =
- getPatternForClassTemplateSpecialization(*this, PointOfInstantiation,
- ClassTemplateSpec, TSK);
+ getPatternForClassTemplateSpecialization(
+ *this, PointOfInstantiation, ClassTemplateSpec, TSK,
+ PrimaryHasMatchedPackOnParmToNonPackOnArg);
if (!Pattern.isUsable())
return Pattern.isInvalid();
diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp
index 8fb9a78caf1051..046cca97a6de30 100644
--- a/clang/test/SemaTemplate/cwg2398.cpp
+++ b/clang/test/SemaTemplate/cwg2398.cpp
@@ -156,16 +156,14 @@ namespace ttp_defaults {
namespace ttp_only {
template <template <class... > class TT1> struct A { static constexpr int V = 0; };
template <template <class > class TT2> struct A<TT2> { static constexpr int V = 1; };
- // new-note at -1 {{partial specialization matches}}
template <template <class, class> class TT3> struct A<TT3> { static constexpr int V = 2; };
- // new-note at -1 {{partial specialization matches}}
template <class ... > struct B;
template <class > struct C;
template <class, class > struct D;
template <class, class, class> struct E;
- static_assert(A<B>::V == 0); // new-error {{ambiguous partial specializations}}
+ static_assert(A<B>::V == 0);
static_assert(A<C>::V == 1);
static_assert(A<D>::V == 2);
static_assert(A<E>::V == 0);
@@ -412,11 +410,9 @@ namespace partial {
template<template<class... T1s> class TT1> struct A {};
template<template<class T2> class TT2> struct A<TT2>;
- // new-note at -1 {{template is declared here}}
template<class... T3s> struct B;
template struct A<B>;
- // new-error at -1 {{explicit instantiation of undefined template}}
} // namespace t1
namespace t2 {
template<template<class... T1s> class TT1> struct A;
More information about the llvm-branch-commits
mailing list