Skip to content

Commit 873043f

Browse files
committed
[clang] deprecate frelaxed-template-template-args, make it on by default
In order to implement this as a DR and avoid breaking reasonable code that worked before P0522, this patch implements a provisional resolution for CWG2398: When deducing template template parameters against each other, and the argument side names a template specialization, instead of just deducing A, we instead deduce a synthesized template template parameter based on A, but with it's parameters using the template specialization's arguments as defaults. The driver flag is deprecated with a warning, and it will not have any effect.
1 parent 1a8935a commit 873043f

18 files changed

+183
-111
lines changed

clang/include/clang/Basic/DiagnosticDriverKinds.td

+1-1
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ def warn_drv_diagnostics_misexpect_requires_pgo : Warning<
435435
def warn_drv_clang_unsupported : Warning<
436436
"the clang compiler does not support '%0'">;
437437
def warn_drv_deprecated_arg : Warning<
438-
"argument '%0' is deprecated, use '%1' instead">, InGroup<Deprecated>;
438+
"argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup<Deprecated>;
439439
def warn_drv_deprecated_custom : Warning<
440440
"argument '%0' is deprecated, %1">, InGroup<Deprecated>;
441441
def warn_drv_assuming_mfloat_abi_is : Warning<

clang/include/clang/Basic/LangOptions.def

-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly")
158158
LANGOPT(Coroutines , 1, 0, "C++20 coroutines")
159159
LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2")
160160
LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods")
161-
LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
162161
LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")
163162

164163
LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")

clang/include/clang/Driver/Options.td

+2-5
Original file line numberDiff line numberDiff line change
@@ -3359,11 +3359,8 @@ defm application_extension : BoolFOption<"application-extension",
33593359
PosFlag<SetTrue, [], [ClangOption, CC1Option],
33603360
"Restrict code to those available for App Extensions">,
33613361
NegFlag<SetFalse>>;
3362-
defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-args",
3363-
LangOpts<"RelaxedTemplateTemplateArgs">, DefaultFalse,
3364-
PosFlag<SetTrue, [], [ClangOption, CC1Option],
3365-
"Enable C++17 relaxed template template argument matching">,
3366-
NegFlag<SetFalse>>;
3362+
def frelaxed_template_template_args : Flag<["-"], "frelaxed-template-template-args">, Flags<[]>;
3363+
def fno_relaxed_template_template_args : Flag<["-"], "fno-relaxed-template-template-args">, Flags<[]>;
33673364
defm sized_deallocation : BoolFOption<"sized-deallocation",
33683365
LangOpts<"SizedDeallocation">, DefaultFalse,
33693366
PosFlag<SetTrue, [], [ClangOption, CC1Option],

clang/lib/Driver/SanitizerArgs.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
797797
Arg->claim();
798798
if (LegacySanitizeCoverage != 0 && DiagnoseErrors) {
799799
D.Diag(diag::warn_drv_deprecated_arg)
800-
<< Arg->getAsString(Args) << "-fsanitize-coverage=trace-pc-guard";
800+
<< Arg->getAsString(Args) << true
801+
<< "-fsanitize-coverage=trace-pc-guard";
801802
}
802803
continue;
803804
}
@@ -833,11 +834,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
833834
// enabled.
834835
if (CoverageFeatures & CoverageTraceBB)
835836
D.Diag(clang::diag::warn_drv_deprecated_arg)
836-
<< "-fsanitize-coverage=trace-bb"
837+
<< "-fsanitize-coverage=trace-bb" << true
837838
<< "-fsanitize-coverage=trace-pc-guard";
838839
if (CoverageFeatures & Coverage8bitCounters)
839840
D.Diag(clang::diag::warn_drv_deprecated_arg)
840-
<< "-fsanitize-coverage=8bit-counters"
841+
<< "-fsanitize-coverage=8bit-counters" << true
841842
<< "-fsanitize-coverage=trace-pc-guard";
842843
}
843844

@@ -849,7 +850,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
849850
if ((CoverageFeatures & InsertionPointTypes) &&
850851
!(CoverageFeatures & InstrumentationTypes) && DiagnoseErrors) {
851852
D.Diag(clang::diag::warn_drv_deprecated_arg)
852-
<< "-fsanitize-coverage=[func|bb|edge]"
853+
<< "-fsanitize-coverage=[func|bb|edge]" << true
853854
<< "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc],["
854855
"control-flow]";
855856
}

clang/lib/Driver/ToolChains/Clang.cpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -6561,7 +6561,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
65616561
if (const Arg *A =
65626562
Args.getLastArg(options::OPT_fvisibility_global_new_delete_hidden)) {
65636563
D.Diag(diag::warn_drv_deprecated_arg)
6564-
<< A->getAsString(Args)
6564+
<< A->getAsString(Args) << true
65656565
<< "-fvisibility-global-new-delete=force-hidden";
65666566
}
65676567

@@ -7288,11 +7288,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
72887288
Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables,
72897289
options::OPT_fno_assume_unique_vtables);
72907290

7291-
// -frelaxed-template-template-args is off by default, as it is a severe
7292-
// breaking change until a corresponding change to template partial ordering
7293-
// is provided.
7294-
Args.addOptInFlag(CmdArgs, options::OPT_frelaxed_template_template_args,
7295-
options::OPT_fno_relaxed_template_template_args);
7291+
// -frelaxed-template-template-args is deprecated, with no effect.
7292+
if (Arg *A = Args.getLastArg(options::OPT_frelaxed_template_template_args,
7293+
options::OPT_fno_relaxed_template_template_args))
7294+
D.Diag(diag::warn_drv_deprecated_arg) << A->getAsString(Args) << false;
72967295

72977296
// -fsized-deallocation is off by default, as it is an ABI-breaking change for
72987297
// most platforms.

clang/lib/Frontend/InitPreprocessor.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -713,8 +713,8 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
713713
}
714714
if (LangOpts.AlignedAllocation && !LangOpts.AlignedAllocationUnavailable)
715715
Builder.defineMacro("__cpp_aligned_new", "201606L");
716-
if (LangOpts.RelaxedTemplateTemplateArgs)
717-
Builder.defineMacro("__cpp_template_template_args", "201611L");
716+
717+
Builder.defineMacro("__cpp_template_template_args", "201611L");
718718

719719
// C++20 features.
720720
if (LangOpts.CPlusPlus20) {

clang/lib/Sema/SemaTemplate.cpp

+41-47
Original file line numberDiff line numberDiff line change
@@ -8343,58 +8343,52 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
83438343
// C++1z [temp.arg.template]p3: (DR 150)
83448344
// A template-argument matches a template template-parameter P when P
83458345
// is at least as specialized as the template-argument A.
8346-
// FIXME: We should enable RelaxedTemplateTemplateArgs by default as it is a
8347-
// defect report resolution from C++17 and shouldn't be introduced by
8348-
// concepts.
8349-
if (getLangOpts().RelaxedTemplateTemplateArgs) {
8350-
// Quick check for the common case:
8351-
// If P contains a parameter pack, then A [...] matches P if each of A's
8352-
// template parameters matches the corresponding template parameter in
8353-
// the template-parameter-list of P.
8354-
if (TemplateParameterListsAreEqual(
8355-
Template->getTemplateParameters(), Params, false,
8356-
TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) &&
8357-
// If the argument has no associated constraints, then the parameter is
8358-
// definitely at least as specialized as the argument.
8359-
// Otherwise - we need a more thorough check.
8360-
!Template->hasAssociatedConstraints())
8361-
return false;
8346+
// Quick check for the common case:
8347+
// If P contains a parameter pack, then A [...] matches P if each of A's
8348+
// template parameters matches the corresponding template parameter in
8349+
// the template-parameter-list of P.
8350+
if (TemplateParameterListsAreEqual(Template->getTemplateParameters(), Params,
8351+
false, TPL_TemplateTemplateArgumentMatch,
8352+
Arg.getLocation()) &&
8353+
// If the argument has no associated constraints, then the parameter is
8354+
// definitely at least as specialized as the argument.
8355+
// Otherwise - we need a more thorough check.
8356+
!Template->hasAssociatedConstraints())
8357+
return false;
83628358

8363-
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
8364-
Arg.getLocation())) {
8365-
// P2113
8366-
// C++20[temp.func.order]p2
8367-
// [...] If both deductions succeed, the partial ordering selects the
8368-
// more constrained template (if one exists) as determined below.
8369-
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
8370-
Params->getAssociatedConstraints(ParamsAC);
8371-
// C++2a[temp.arg.template]p3
8372-
// [...] In this comparison, if P is unconstrained, the constraints on A
8373-
// are not considered.
8374-
if (ParamsAC.empty())
8375-
return false;
8359+
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
8360+
Arg.getLocation())) {
8361+
// P2113
8362+
// C++20[temp.func.order]p2
8363+
// [...] If both deductions succeed, the partial ordering selects the
8364+
// more constrained template (if one exists) as determined below.
8365+
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
8366+
Params->getAssociatedConstraints(ParamsAC);
8367+
// C++2a[temp.arg.template]p3
8368+
// [...] In this comparison, if P is unconstrained, the constraints on A
8369+
// are not considered.
8370+
if (ParamsAC.empty())
8371+
return false;
83768372

8377-
Template->getAssociatedConstraints(TemplateAC);
8373+
Template->getAssociatedConstraints(TemplateAC);
83788374

8379-
bool IsParamAtLeastAsConstrained;
8380-
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
8381-
IsParamAtLeastAsConstrained))
8382-
return true;
8383-
if (!IsParamAtLeastAsConstrained) {
8384-
Diag(Arg.getLocation(),
8385-
diag::err_template_template_parameter_not_at_least_as_constrained)
8386-
<< Template << Param << Arg.getSourceRange();
8387-
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
8388-
Diag(Template->getLocation(), diag::note_entity_declared_at)
8389-
<< Template;
8390-
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
8391-
TemplateAC);
8392-
return true;
8393-
}
8394-
return false;
8375+
bool IsParamAtLeastAsConstrained;
8376+
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
8377+
IsParamAtLeastAsConstrained))
8378+
return true;
8379+
if (!IsParamAtLeastAsConstrained) {
8380+
Diag(Arg.getLocation(),
8381+
diag::err_template_template_parameter_not_at_least_as_constrained)
8382+
<< Template << Param << Arg.getSourceRange();
8383+
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
8384+
Diag(Template->getLocation(), diag::note_entity_declared_at) << Template;
8385+
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
8386+
TemplateAC);
8387+
return true;
83958388
}
8396-
// FIXME: Produce better diagnostics for deduction failures.
8389+
return false;
83978390
}
8391+
// FIXME: Produce better diagnostics for deduction failures.
83988392

83998393
return !TemplateParameterListsAreEqual(Template->getTemplateParameters(),
84008394
Params,

clang/lib/Sema/SemaTemplateDeduction.cpp

+93-6
Original file line numberDiff line numberDiff line change
@@ -507,10 +507,62 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument(
507507
S, TemplateParams, NTTP, DeducedTemplateArgument(New), T, Info, Deduced);
508508
}
509509

510+
static NamedDecl *DeduceTemplateArguments(Sema &S, NamedDecl *A,
511+
TemplateArgument Default) {
512+
switch (A->getKind()) {
513+
case Decl::TemplateTypeParm: {
514+
auto *T = cast<TemplateTypeParmDecl>(A);
515+
// FIXME: DefaultArgument can't represent a pack.
516+
if (T->isParameterPack())
517+
return A;
518+
auto *R = TemplateTypeParmDecl::Create(
519+
S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
520+
T->getDepth(), T->getIndex(), T->getIdentifier(),
521+
T->wasDeclaredWithTypename(), /*ParameterPack=*/false,
522+
T->hasTypeConstraint());
523+
R->setDefaultArgument(
524+
S.Context.getTrivialTypeSourceInfo(Default.getAsType()));
525+
if (R->hasTypeConstraint()) {
526+
auto *C = R->getTypeConstraint();
527+
R->setTypeConstraint(C->getConceptReference(),
528+
C->getImmediatelyDeclaredConstraint());
529+
}
530+
return R;
531+
}
532+
case Decl::NonTypeTemplateParm: {
533+
auto *T = cast<NonTypeTemplateParmDecl>(A);
534+
// FIXME: DefaultArgument can't represent a pack.
535+
if (T->isParameterPack())
536+
return A;
537+
auto *R = NonTypeTemplateParmDecl::Create(
538+
S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
539+
T->getDepth(), T->getIndex(), T->getIdentifier(), T->getType(),
540+
/*ParameterPack=*/false, T->getTypeSourceInfo());
541+
R->setDefaultArgument(Default.getAsExpr());
542+
if (auto *PTC = T->getPlaceholderTypeConstraint())
543+
R->setPlaceholderTypeConstraint(PTC);
544+
return R;
545+
}
546+
case Decl::TemplateTemplateParm: {
547+
auto *T = cast<TemplateTemplateParmDecl>(A);
548+
auto *R = TemplateTemplateParmDecl::Create(
549+
S.Context, A->getDeclContext(), SourceLocation(), T->getDepth(),
550+
T->getIndex(), T->isParameterPack(), T->getIdentifier(),
551+
T->wasDeclaredWithTypename(), T->getTemplateParameters());
552+
R->setDefaultArgument(
553+
S.Context, TemplateArgumentLoc(Default, TemplateArgumentLocInfo()));
554+
return R;
555+
}
556+
default:
557+
llvm_unreachable("Unexpected Decl Kind");
558+
}
559+
}
560+
510561
static TemplateDeductionResult
511562
DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
512563
TemplateName Param, TemplateName Arg,
513564
TemplateDeductionInfo &Info,
565+
ArrayRef<TemplateArgument> DefaultArguments,
514566
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
515567
TemplateDecl *ParamDecl = Param.getAsTemplateDecl();
516568
if (!ParamDecl) {
@@ -519,13 +571,45 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
519571
return TemplateDeductionResult::Success;
520572
}
521573

522-
if (TemplateTemplateParmDecl *TempParam
523-
= dyn_cast<TemplateTemplateParmDecl>(ParamDecl)) {
574+
if (auto *TempParam = dyn_cast<TemplateTemplateParmDecl>(ParamDecl)) {
524575
// If we're not deducing at this depth, there's nothing to deduce.
525576
if (TempParam->getDepth() != Info.getDeducedDepth())
526577
return TemplateDeductionResult::Success;
527578

528-
DeducedTemplateArgument NewDeduced(S.Context.getCanonicalTemplateName(Arg));
579+
auto NewDeduced = DeducedTemplateArgument(Arg);
580+
// Provisional resolution for CWG2398: If Arg is also a template template
581+
// param, and it names a template specialization, then we deduce a
582+
// synthesized template template parameter based on A, but using the TS's
583+
// arguments as defaults.
584+
if (auto *TempArg = dyn_cast_or_null<TemplateTemplateParmDecl>(
585+
Arg.getAsTemplateDecl())) {
586+
assert(Arg.getKind() == TemplateName::Template);
587+
assert(!TempArg->isExpandedParameterPack());
588+
589+
TemplateParameterList *As = TempArg->getTemplateParameters();
590+
if (DefaultArguments.size() != 0) {
591+
assert(DefaultArguments.size() <= As->size());
592+
SmallVector<NamedDecl *, 4> Params(As->size());
593+
for (unsigned I = 0; I < DefaultArguments.size(); ++I)
594+
Params[I] =
595+
DeduceTemplateArguments(S, As->getParam(I), DefaultArguments[I]);
596+
for (unsigned I = DefaultArguments.size(); I < As->size(); ++I)
597+
Params[I] = As->getParam(I);
598+
// FIXME: We could unique these, and also the parameters, but we don't
599+
// expect programs to contain a large enough amount of these deductions
600+
// for that to be worthwhile.
601+
auto *TPL = TemplateParameterList::Create(
602+
S.Context, SourceLocation(), SourceLocation(), Params,
603+
SourceLocation(), As->getRequiresClause());
604+
NewDeduced = DeducedTemplateArgument(
605+
TemplateName(TemplateTemplateParmDecl::Create(
606+
S.Context, TempArg->getDeclContext(), SourceLocation(),
607+
TempArg->getDepth(), TempArg->getPosition(),
608+
TempArg->isParameterPack(), TempArg->getIdentifier(),
609+
TempArg->wasDeclaredWithTypename(), TPL)));
610+
}
611+
}
612+
529613
DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,
530614
Deduced[TempParam->getIndex()],
531615
NewDeduced);
@@ -604,7 +688,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
604688

605689
// Perform template argument deduction for the template name.
606690
if (auto Result =
607-
DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, Deduced);
691+
DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info,
692+
SA->template_arguments(), Deduced);
608693
Result != TemplateDeductionResult::Success)
609694
return Result;
610695
// Perform template argument deduction on each template
@@ -630,7 +715,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
630715
// Perform template argument deduction for the template name.
631716
if (auto Result = DeduceTemplateArguments(
632717
S, TemplateParams, TP->getTemplateName(),
633-
TemplateName(SA->getSpecializedTemplate()), Info, Deduced);
718+
TemplateName(SA->getSpecializedTemplate()), Info,
719+
SA->getTemplateArgs().asArray(), Deduced);
634720
Result != TemplateDeductionResult::Success)
635721
return Result;
636722

@@ -2320,7 +2406,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
23202406
case TemplateArgument::Template:
23212407
if (A.getKind() == TemplateArgument::Template)
23222408
return DeduceTemplateArguments(S, TemplateParams, P.getAsTemplate(),
2323-
A.getAsTemplate(), Info, Deduced);
2409+
A.getAsTemplate(), Info,
2410+
/*DefaultArguments=*/{}, Deduced);
23242411
Info.FirstArg = P;
23252412
Info.SecondArg = A;
23262413
return TemplateDeductionResult::NonDeducedMismatch;

clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++2a -frelaxed-template-template-args -verify %s
1+
// RUN: %clang_cc1 -std=c++2a -verify %s
22

33
template<typename T> concept C = T::f(); // #C
44
template<typename T> concept D = C<T> && T::g();

clang/test/CodeGenCXX/mangle-concept.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -verify -frelaxed-template-template-args -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s
2-
// RUN: %clang_cc1 -verify -frelaxed-template-template-args -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=16 | FileCheck %s --check-prefix=CLANG16
1+
// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s
2+
// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=16 | FileCheck %s --check-prefix=CLANG16
33
// expected-no-diagnostics
44

55
namespace test1 {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// RUN: %clang -fsyntax-only -frelaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-ON %s
2+
// RUN: %clang -fsyntax-only -fno-relaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-OFF %s
3+
4+
// CHECK-ON: warning: argument '-frelaxed-template-template-args' is deprecated [-Wdeprecated]
5+
// CHECK-OFF: warning: argument '-fno-relaxed-template-template-args' is deprecated [-Wdeprecated]

clang/test/Lexer/cxx-features.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -fsized-deallocation -verify %s
88

99
//
10-
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -frelaxed-template-template-args -DRELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s
1110
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -DCONCEPTS_TS=1 -verify %s
1211
// RUN: %clang_cc1 -std=c++14 -fno-rtti -fno-threadsafe-statics -verify %s -DNO_EXCEPTIONS -DNO_RTTI -DNO_THREADSAFE_STATICS -fsized-deallocation
1312
// RUN: %clang_cc1 -std=c++14 -fchar8_t -DNO_EXCEPTIONS -DCHAR8_T -verify -fsized-deallocation %s
@@ -231,9 +230,7 @@
231230
#error "wrong value for __cpp_nontype_template_args"
232231
#endif
233232

234-
#if defined(RELAXED_TEMPLATE_TEMPLATE_ARGS) \
235-
? check(template_template_args, 0, 0, 0, 201611, 201611, 201611, 201611) \
236-
: check(template_template_args, 0, 0, 0, 0, 0, 0, 0)
233+
#if check(template_template_args, 201611, 201611, 201611, 201611, 201611, 201611, 201611)
237234
#error "wrong value for __cpp_template_template_args"
238235
#endif
239236

0 commit comments

Comments
 (0)