Skip to content

Commit

Permalink
[clang] Enable C++17 relaxed template template argument matching by d…
Browse files Browse the repository at this point in the history
…efault (#89807)

This patch will finally allow us to mark C++17 support in clang as
complete.
    
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 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.
    
Fixes #36505
  • Loading branch information
mizvekov committed May 2, 2024
1 parent 59ab292 commit b86e099
Show file tree
Hide file tree
Showing 19 changed files with 317 additions and 63 deletions.
19 changes: 19 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ C++ Specific Potentially Breaking Changes
- Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
This error can be disabled via `-Wno-strict-primary-template-shadow` for compatibility with previous versions of clang.

- The behavior controlled by the `-frelaxed-template-template-args` flag is now
on by default, and the flag is deprecated. Until the flag is finally removed,
it's negative spelling can be used to obtain compatibility with previous
versions of clang.

ABI Changes in This Version
---------------------------
- Fixed Microsoft name mangling of implicitly defined variables used for thread
Expand Down Expand Up @@ -94,6 +99,17 @@ sections with improvements to Clang's support for those languages.

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

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

- P0522 implementation is enabled by default in all language versions, and
provisional wording for CWG2398 is implemented.

C Language Changes
------------------

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticDriverKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ def warn_drv_diagnostics_misexpect_requires_pgo : Warning<
def warn_drv_clang_unsupported : Warning<
"the clang compiler does not support '%0'">;
def warn_drv_deprecated_arg : Warning<
"argument '%0' is deprecated, use '%1' instead">, InGroup<Deprecated>;
"argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup<Deprecated>;
def warn_drv_deprecated_custom : Warning<
"argument '%0' is deprecated, %1">, InGroup<Deprecated>;
def warn_drv_assuming_mfloat_abi_is : Warning<
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly")
LANGOPT(Coroutines , 1, 0, "C++20 coroutines")
LANGOPT(CoroAlignedAllocation, 1, 0, "prefer Aligned Allocation according to P2014 Option 2")
LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods")
LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
LANGOPT(RelaxedTemplateTemplateArgs, 1, 1, "C++17 relaxed matching of template template arguments")
LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")

LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")
Expand Down
8 changes: 4 additions & 4 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3383,10 +3383,10 @@ defm application_extension : BoolFOption<"application-extension",
"Restrict code to those available for App Extensions">,
NegFlag<SetFalse>>;
defm relaxed_template_template_args : BoolFOption<"relaxed-template-template-args",
LangOpts<"RelaxedTemplateTemplateArgs">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Enable C++17 relaxed template template argument matching">,
NegFlag<SetFalse>>;
LangOpts<"RelaxedTemplateTemplateArgs">, DefaultTrue,
PosFlag<SetTrue, [], [], "Enable">,
NegFlag<SetFalse, [], [CC1Option], "Disable">,
BothFlags<[], [ClangOption], " C++17 relaxed template template argument matching">>;
defm sized_deallocation : BoolFOption<"sized-deallocation",
LangOpts<"SizedDeallocation">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
Expand Down
9 changes: 5 additions & 4 deletions clang/lib/Driver/SanitizerArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
Arg->claim();
if (LegacySanitizeCoverage != 0 && DiagnoseErrors) {
D.Diag(diag::warn_drv_deprecated_arg)
<< Arg->getAsString(Args) << "-fsanitize-coverage=trace-pc-guard";
<< Arg->getAsString(Args) << /*hasReplacement=*/true
<< "-fsanitize-coverage=trace-pc-guard";
}
continue;
}
Expand Down Expand Up @@ -833,11 +834,11 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
// enabled.
if (CoverageFeatures & CoverageTraceBB)
D.Diag(clang::diag::warn_drv_deprecated_arg)
<< "-fsanitize-coverage=trace-bb"
<< "-fsanitize-coverage=trace-bb" << /*hasReplacement=*/true
<< "-fsanitize-coverage=trace-pc-guard";
if (CoverageFeatures & Coverage8bitCounters)
D.Diag(clang::diag::warn_drv_deprecated_arg)
<< "-fsanitize-coverage=8bit-counters"
<< "-fsanitize-coverage=8bit-counters" << /*hasReplacement=*/true
<< "-fsanitize-coverage=trace-pc-guard";
}

Expand All @@ -849,7 +850,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
if ((CoverageFeatures & InsertionPointTypes) &&
!(CoverageFeatures & InstrumentationTypes) && DiagnoseErrors) {
D.Diag(clang::diag::warn_drv_deprecated_arg)
<< "-fsanitize-coverage=[func|bb|edge]"
<< "-fsanitize-coverage=[func|bb|edge]" << /*hasReplacement=*/true
<< "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc],["
"control-flow]";
}
Expand Down
16 changes: 10 additions & 6 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6513,7 +6513,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (const Arg *A =
Args.getLastArg(options::OPT_fvisibility_global_new_delete_hidden)) {
D.Diag(diag::warn_drv_deprecated_arg)
<< A->getAsString(Args)
<< A->getAsString(Args) << /*hasReplacement=*/true
<< "-fvisibility-global-new-delete=force-hidden";
}

Expand Down Expand Up @@ -7240,11 +7240,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.addOptOutFlag(CmdArgs, options::OPT_fassume_unique_vtables,
options::OPT_fno_assume_unique_vtables);

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

// -fsized-deallocation is off by default, as it is an ABI-breaking change for
// most platforms.
Expand Down
3 changes: 0 additions & 3 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8334,9 +8334,6 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
// C++1z [temp.arg.template]p3: (DR 150)
// A template-argument matches a template template-parameter P when P
// is at least as specialized as the template-argument A.
// FIXME: We should enable RelaxedTemplateTemplateArgs by default as it is a
// defect report resolution from C++17 and shouldn't be introduced by
// concepts.
if (getLangOpts().RelaxedTemplateTemplateArgs) {
// Quick check for the common case:
// If P contains a parameter pack, then A [...] matches P if each of A's
Expand Down
107 changes: 101 additions & 6 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,10 +507,70 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument(
S, TemplateParams, NTTP, DeducedTemplateArgument(New), T, Info, Deduced);
}

/// Create a shallow copy of a given template parameter declaration, with
/// empty source locations and using the given TemplateArgument as it's
/// default argument.
///
/// \returns The new template parameter declaration.
static NamedDecl *getTemplateParameterWithDefault(Sema &S, NamedDecl *A,
TemplateArgument Default) {
switch (A->getKind()) {
case Decl::TemplateTypeParm: {
auto *T = cast<TemplateTypeParmDecl>(A);
// FIXME: A TemplateTypeParmDecl's DefaultArgument can't hold a full
// TemplateArgument, so there is currently no way to specify a pack as a
// default argument for these.
if (T->isParameterPack())
return A;
auto *R = TemplateTypeParmDecl::Create(
S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
T->getDepth(), T->getIndex(), T->getIdentifier(),
T->wasDeclaredWithTypename(), /*ParameterPack=*/false,
T->hasTypeConstraint());
R->setDefaultArgument(
S.Context.getTrivialTypeSourceInfo(Default.getAsType()));
if (R->hasTypeConstraint()) {
auto *C = R->getTypeConstraint();
R->setTypeConstraint(C->getConceptReference(),
C->getImmediatelyDeclaredConstraint());
}
return R;
}
case Decl::NonTypeTemplateParm: {
auto *T = cast<NonTypeTemplateParmDecl>(A);
// FIXME: Ditto, as above for TemplateTypeParm case.
if (T->isParameterPack())
return A;
auto *R = NonTypeTemplateParmDecl::Create(
S.Context, A->getDeclContext(), SourceLocation(), SourceLocation(),
T->getDepth(), T->getIndex(), T->getIdentifier(), T->getType(),
/*ParameterPack=*/false, T->getTypeSourceInfo());
R->setDefaultArgument(Default.getAsExpr());
if (auto *PTC = T->getPlaceholderTypeConstraint())
R->setPlaceholderTypeConstraint(PTC);
return R;
}
case Decl::TemplateTemplateParm: {
auto *T = cast<TemplateTemplateParmDecl>(A);
auto *R = TemplateTemplateParmDecl::Create(
S.Context, A->getDeclContext(), SourceLocation(), T->getDepth(),
T->getIndex(), T->isParameterPack(), T->getIdentifier(),
T->wasDeclaredWithTypename(), T->getTemplateParameters());
R->setDefaultArgument(
S.Context,
S.getTrivialTemplateArgumentLoc(Default, QualType(), SourceLocation()));
return R;
}
default:
llvm_unreachable("Unexpected Decl Kind");
}
}

static TemplateDeductionResult
DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
TemplateName Param, TemplateName Arg,
TemplateDeductionInfo &Info,
ArrayRef<TemplateArgument> DefaultArguments,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
TemplateDecl *ParamDecl = Param.getAsTemplateDecl();
if (!ParamDecl) {
Expand All @@ -519,13 +579,45 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
return TemplateDeductionResult::Success;
}

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

DeducedTemplateArgument NewDeduced(S.Context.getCanonicalTemplateName(Arg));
auto NewDeduced = DeducedTemplateArgument(Arg);
// Provisional resolution for CWG2398: If Arg is also a template template
// param, and it names a template specialization, then we deduce a
// synthesized template template parameter based on A, but using the TS's
// arguments as defaults.
if (auto *TempArg = dyn_cast_or_null<TemplateTemplateParmDecl>(
Arg.getAsTemplateDecl())) {
assert(Arg.getKind() == TemplateName::Template);
assert(!TempArg->isExpandedParameterPack());

TemplateParameterList *As = TempArg->getTemplateParameters();
if (DefaultArguments.size() != 0) {
assert(DefaultArguments.size() <= As->size());
SmallVector<NamedDecl *, 4> Params(As->size());
for (unsigned I = 0; I < DefaultArguments.size(); ++I)
Params[I] = getTemplateParameterWithDefault(S, As->getParam(I),
DefaultArguments[I]);
for (unsigned I = DefaultArguments.size(); I < As->size(); ++I)
Params[I] = As->getParam(I);
// FIXME: We could unique these, and also the parameters, but we don't
// expect programs to contain a large enough amount of these deductions
// for that to be worthwhile.
auto *TPL = TemplateParameterList::Create(
S.Context, SourceLocation(), SourceLocation(), Params,
SourceLocation(), As->getRequiresClause());
NewDeduced = DeducedTemplateArgument(
TemplateName(TemplateTemplateParmDecl::Create(
S.Context, TempArg->getDeclContext(), SourceLocation(),
TempArg->getDepth(), TempArg->getPosition(),
TempArg->isParameterPack(), TempArg->getIdentifier(),
TempArg->wasDeclaredWithTypename(), TPL)));
}
}

DeducedTemplateArgument Result = checkDeducedTemplateArguments(S.Context,
Deduced[TempParam->getIndex()],
NewDeduced);
Expand Down Expand Up @@ -604,7 +696,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,

// Perform template argument deduction for the template name.
if (auto Result =
DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info, Deduced);
DeduceTemplateArguments(S, TemplateParams, TNP, TNA, Info,
SA->template_arguments(), Deduced);
Result != TemplateDeductionResult::Success)
return Result;
// Perform template argument deduction on each template
Expand All @@ -630,7 +723,8 @@ DeduceTemplateSpecArguments(Sema &S, TemplateParameterList *TemplateParams,
// Perform template argument deduction for the template name.
if (auto Result = DeduceTemplateArguments(
S, TemplateParams, TP->getTemplateName(),
TemplateName(SA->getSpecializedTemplate()), Info, Deduced);
TemplateName(SA->getSpecializedTemplate()), Info,
SA->getTemplateArgs().asArray(), Deduced);
Result != TemplateDeductionResult::Success)
return Result;

Expand Down Expand Up @@ -2323,7 +2417,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
case TemplateArgument::Template:
if (A.getKind() == TemplateArgument::Template)
return DeduceTemplateArguments(S, TemplateParams, P.getAsTemplate(),
A.getAsTemplate(), Info, Deduced);
A.getAsTemplate(), Info,
/*DefaultArguments=*/{}, Deduced);
Info.FirstArg = P;
Info.SecondArg = A;
return TemplateDeductionResult::NonDeducedMismatch;
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++2a -frelaxed-template-template-args -verify %s
// RUN: %clang_cc1 -std=c++2a -verify %s

template<typename T> concept C = T::f(); // #C
template<typename T> concept D = C<T> && T::g();
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGenCXX/mangle-concept.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// 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
// 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
// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=latest | FileCheck %s
// RUN: %clang_cc1 -verify -std=c++20 -emit-llvm -triple %itanium_abi_triple -o - %s -fclang-abi-compat=16 | FileCheck %s --check-prefix=CLANG16
// expected-no-diagnostics

namespace test1 {
Expand Down
5 changes: 5 additions & 0 deletions clang/test/Driver/frelaxed-template-template-args.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: %clang -fsyntax-only -frelaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-ON %s
// RUN: %clang -fsyntax-only -fno-relaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-OFF %s

// CHECK-ON: warning: argument '-frelaxed-template-template-args' is deprecated [-Wdeprecated]
// CHECK-OFF: warning: argument '-fno-relaxed-template-template-args' is deprecated [-Wdeprecated]
6 changes: 3 additions & 3 deletions clang/test/Lexer/cxx-features.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -fsized-deallocation -verify %s

//
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -frelaxed-template-template-args -DRELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -fno-relaxed-template-template-args -DNO_RELAXED_TEMPLATE_TEMPLATE_ARGS=1 -verify %s
// RUN: %clang_cc1 -std=c++17 -fcxx-exceptions -fsized-deallocation -DCONCEPTS_TS=1 -verify %s
// RUN: %clang_cc1 -std=c++14 -fno-rtti -fno-threadsafe-statics -verify %s -DNO_EXCEPTIONS -DNO_RTTI -DNO_THREADSAFE_STATICS -fsized-deallocation
// RUN: %clang_cc1 -std=c++14 -fchar8_t -DNO_EXCEPTIONS -DCHAR8_T -verify -fsized-deallocation %s
Expand Down Expand Up @@ -231,8 +231,8 @@
#error "wrong value for __cpp_nontype_template_args"
#endif

#if defined(RELAXED_TEMPLATE_TEMPLATE_ARGS) \
? check(template_template_args, 0, 0, 0, 201611, 201611, 201611, 201611) \
#if !defined(NO_RELAXED_TEMPLATE_TEMPLATE_ARGS) \
? check(template_template_args, 201611, 201611, 201611, 201611, 201611, 201611, 201611) \
: check(template_template_args, 0, 0, 0, 0, 0, 0, 0)
#error "wrong value for __cpp_template_template_args"
#endif
Expand Down

0 comments on commit b86e099

Please sign in to comment.