Skip to content

Commit 4ee58ef

Browse files
committed
[clang] Enable C++17 relaxed template template argument matching 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. With this patch, we finally mark C++17 support in clang as complete. Fixes #36505
1 parent 2c1c887 commit 4ee58ef

19 files changed

+293
-63
lines changed

clang/docs/ReleaseNotes.rst

+19
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ C++ Specific Potentially Breaking Changes
4848
- Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
4949
This error can be disabled via `-Wno-strict-primary-template-shadow` for compatibility with previous versions of clang.
5050

51+
- The behavior controlled by the `-frelaxed-template-template-args` flag is now
52+
on by default, and the flag is deprecated. Until the flag is finally removed,
53+
it's negative spelling can be used to obtain compatibility with previous
54+
versions of clang.
55+
5156
ABI Changes in This Version
5257
---------------------------
5358
- Fixed Microsoft name mangling of implicitly defined variables used for thread
@@ -88,6 +93,17 @@ sections with improvements to Clang's support for those languages.
8893

8994
C++ Language Changes
9095
--------------------
96+
- C++17 support is now completed, with the enablement of the
97+
relaxed temlate template argument matching rules introduced in P0522,
98+
which was retroactively applied as a defect report.
99+
While the implementation already existed since Clang 4, it was turned off by
100+
default, and was controlled with the `-frelaxed-template-template-args` flag.
101+
In this release, we implement provisional wording for a core defect on
102+
P0522 (CWG2398), which avoids the most serious compatibility issues caused
103+
by it, allowing us to enable it by default in this release.
104+
The flag is now deprecated, and will be removed in the next release, but can
105+
still be used to turn it off and regain compatibility with previous versions
106+
(#GH36505).
91107
- Implemented ``_BitInt`` literal suffixes ``__wb`` or ``__WB`` as a Clang extension with ``unsigned`` modifiers also allowed. (#GH85223).
92108

93109
C++17 Feature Support
@@ -164,6 +180,9 @@ Resolutions to C++ Defect Reports
164180
- Clang now diagnoses declarative nested-name-specifiers with pack-index-specifiers.
165181
(`CWG2858: Declarative nested-name-specifiers and pack-index-specifiers <https://cplusplus.github.io/CWG/issues/2858.html>`_).
166182

183+
- P0522 implementation is enabled by default in all language versions, and
184+
provisional wording for CWG2398 is implemented.
185+
167186
C Language Changes
168187
------------------
169188

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-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ 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")
161+
LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments")
162162
LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")
163163

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

clang/include/clang/Driver/Options.td

+4-4
Original file line numberDiff line numberDiff line change
@@ -3365,10 +3365,10 @@ defm application_extension : BoolFOption<"application-extension",
33653365
"Restrict code to those available for App Extensions">,
33663366
NegFlag<SetFalse>>;
33673367
defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-args",
3368-
LangOpts<"RelaxedTemplateTemplateArgs">, DefaultFalse,
3369-
PosFlag<SetTrue, [], [ClangOption, CC1Option],
3370-
"Enable C++17 relaxed template template argument matching">,
3371-
NegFlag<SetFalse>>;
3368+
LangOpts<"RelaxedTemplateTemplateArgs">, DefaultTrue,
3369+
PosFlag<SetTrue, [], [], "Enable">,
3370+
NegFlag<SetFalse, [], [CC1Option], "Disable">,
3371+
BothFlags<[], [ClangOption], " C++17 relaxed template template argument matching">>;
33723372
defm sized_deallocation : BoolFOption<"sized-deallocation",
33733373
LangOpts<"SizedDeallocation">, DefaultFalse,
33743374
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) << /*hasReplacement=*/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" << /*hasReplacement=*/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" << /*hasReplacement=*/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]" << /*hasReplacement=*/true
853854
<< "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc],["
854855
"control-flow]";
855856
}

clang/lib/Driver/ToolChains/Clang.cpp

+10-6
Original file line numberDiff line numberDiff line change
@@ -6529,7 +6529,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
65296529
if (const Arg *A =
65306530
Args.getLastArg(options::OPT_fvisibility_global_new_delete_hidden)) {
65316531
D.Diag(diag::warn_drv_deprecated_arg)
6532-
<< A->getAsString(Args)
6532+
<< A->getAsString(Args) << /*hasReplacement=*/true
65336533
<< "-fvisibility-global-new-delete=force-hidden";
65346534
}
65356535

@@ -7256,11 +7256,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
72567256
Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables,
72577257
options::OPT_fno_assume_unique_vtables);
72587258

7259-
// -frelaxed-template-template-args is off by default, as it is a severe
7260-
// breaking change until a corresponding change to template partial ordering
7261-
// is provided.
7262-
Args.addOptInFlag(CmdArgs, options::OPT_frelaxed_template_template_args,
7263-
options::OPT_fno_relaxed_template_template_args);
7259+
// -frelaxed-template-template-args is deprecated.
7260+
if (Arg *A =
7261+
Args.getLastArg(options::OPT_frelaxed_template_template_args,
7262+
options::OPT_fno_relaxed_template_template_args)) {
7263+
D.Diag(diag::warn_drv_deprecated_arg)
7264+
<< A->getAsString(Args) << /*hasReplacement=*/false;
7265+
if (A->getOption().matches(options::OPT_fno_relaxed_template_template_args))
7266+
CmdArgs.push_back("-fno-relaxed-template-template-args");
7267+
}
72647268

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

clang/lib/Sema/SemaTemplate.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -8343,9 +8343,6 @@ 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.
83498346
if (getLangOpts().RelaxedTemplateTemplateArgs) {
83508347
// Quick check for the common case:
83518348
// If P contains a parameter pack, then A [...] matches P if each of A's

clang/lib/Sema/SemaTemplateDeduction.cpp

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

510+
/// Create a shallow copy of a given template parameter declaration, with
511+
/// empty source locations and using the given TemplateArgument as it's
512+
/// default argument.
513+
///
514+
/// \returns The new template parameter declaration.
515+
static NamedDecl *getTemplateParameterWithDefault(Sema &S, NamedDecl *A,
516+
TemplateArgument Default) {
517+
switch (A->getKind()) {
518+
case Decl::TemplateTypeParm: {
519+
auto *T = cast<TemplateTypeParmDecl>(A);
520+
// FIXME: A TemplateTypeParmDecl's DefaultArgument can't hold a full
521+
// TemplateArgument, so there is currently no way to specify a pack as a
522+
// default argument for these.
523+
if (T->isParameterPack())
524+
return A;
525+
auto *R = TemplateTypeParmDecl::Create(
526+
S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
527+
T->getDepth(), T->getIndex(), T->getIdentifier(),
528+
T->wasDeclaredWithTypename(), /*ParameterPack=*/false,
529+
T->hasTypeConstraint());
530+
R->setDefaultArgument(
531+
S.Context.getTrivialTypeSourceInfo(Default.getAsType()));
532+
if (R->hasTypeConstraint()) {
533+
auto *C = R->getTypeConstraint();
534+
R->setTypeConstraint(C->getConceptReference(),
535+
C->getImmediatelyDeclaredConstraint());
536+
}
537+
return R;
538+
}
539+
case Decl::NonTypeTemplateParm: {
540+
auto *T = cast<NonTypeTemplateParmDecl>(A);
541+
// FIXME: Ditto, as above for TemplateTypeParm case.
542+
if (T->isParameterPack())
543+
return A;
544+
auto *R = NonTypeTemplateParmDecl::Create(
545+
S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
546+
T->getDepth(), T->getIndex(), T->getIdentifier(), T->getType(),
547+
/*ParameterPack=*/false, T->getTypeSourceInfo());
548+
R->setDefaultArgument(Default.getAsExpr());
549+
if (auto *PTC = T->getPlaceholderTypeConstraint())
550+
R->setPlaceholderTypeConstraint(PTC);
551+
return R;
552+
}
553+
case Decl::TemplateTemplateParm: {
554+
auto *T = cast<TemplateTemplateParmDecl>(A);
555+
auto *R = TemplateTemplateParmDecl::Create(
556+
S.Context, A->getDeclContext(), SourceLocation(), T->getDepth(),
557+
T->getIndex(), T->isParameterPack(), T->getIdentifier(),
558+
T->wasDeclaredWithTypename(), T->getTemplateParameters());
559+
R->setDefaultArgument(
560+
S.Context,
561+
S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation()));
562+
return R;
563+
}
564+
default:
565+
llvm_unreachable("Unexpected Decl Kind");
566+
}
567+
}
568+
510569
static TemplateDeductionResult
511570
DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
512571
TemplateName Param, TemplateName Arg,
513572
TemplateDeductionInfo &Info,
573+
ArrayRef<TemplateArgument> DefaultArguments,
514574
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
515575
TemplateDecl *ParamDecl = Param.getAsTemplateDecl();
516576
if (!ParamDecl) {
@@ -519,13 +579,45 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
519579
return TemplateDeductionResult::Success;
520580
}
521581

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

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

605697
// Perform template argument deduction for the template name.
606698
if (auto Result =
607-
DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, Deduced);
699+
DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info,
700+
SA->template_arguments(), Deduced);
608701
Result != TemplateDeductionResult::Success)
609702
return Result;
610703
// Perform template argument deduction on each template
@@ -630,7 +723,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
630723
// Perform template argument deduction for the template name.
631724
if (auto Result = DeduceTemplateArguments(
632725
S, TemplateParams, TP->getTemplateName(),
633-
TemplateName(SA->getSpecializedTemplate()), Info, Deduced);
726+
TemplateName(SA->getSpecializedTemplate()), Info,
727+
SA->getTemplateArgs().asArray(), Deduced);
634728
Result != TemplateDeductionResult::Success)
635729
return Result;
636730

@@ -2323,7 +2417,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
23232417
case TemplateArgument::Template:
23242418
if (A.getKind() == TemplateArgument::Template)
23252419
return DeduceTemplateArguments(S, TemplateParams, P.getAsTemplate(),
2326-
A.getAsTemplate(), Info, Deduced);
2420+
A.getAsTemplate(), Info,
2421+
/*DefaultArguments=*/{}, Deduced);
23272422
Info.FirstArg = P;
23282423
Info.SecondArg = A;
23292424
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

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
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
10+
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -fno-relaxed-template-template-args -DNO_RELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s
1111
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -DCONCEPTS_TS=1 -verify %s
1212
// RUN: %clang_cc1 -std=c++14 -fno-rtti -fno-threadsafe-statics -verify %s -DNO_EXCEPTIONS -DNO_RTTI -DNO_THREADSAFE_STATICS -fsized-deallocation
1313
// RUN: %clang_cc1 -std=c++14 -fchar8_t -DNO_EXCEPTIONS -DCHAR8_T -verify -fsized-deallocation %s
@@ -231,8 +231,8 @@
231231
#error "wrong value for __cpp_nontype_template_args"
232232
#endif
233233

234-
#if defined(RELAXED_TEMPLATE_TEMPLATE_ARGS) \
235-
? check(template_template_args, 0, 0, 0, 201611, 201611, 201611, 201611) \
234+
#if !defined(NO_RELAXED_TEMPLATE_TEMPLATE_ARGS) \
235+
? check(template_template_args, 201611, 201611, 201611, 201611, 201611, 201611, 201611) \
236236
: check(template_template_args, 0, 0, 0, 0, 0, 0, 0)
237237
#error "wrong value for __cpp_template_template_args"
238238
#endif

0 commit comments

Comments
 (0)