Skip to content

Commit

Permalink
[clang] deprecate frelaxed-template-template-args, make it on by default
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mizvekov committed Apr 23, 2024
1 parent 1a8935a commit 873043f
Show file tree
Hide file tree
Showing 18 changed files with 183 additions and 111 deletions.
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
1 change: 0 additions & 1 deletion clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ 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(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features")

LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")
Expand Down
7 changes: 2 additions & 5 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3359,11 +3359,8 @@ defm application_extension : BoolFOption<"application-extension",
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"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>>;
def frelaxed_template_template_args : Flag<["-"], "frelaxed-template-template-args">, Flags<[]>;
def fno_relaxed_template_template_args : Flag<["-"], "fno-relaxed-template-template-args">, Flags<[]>;
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) << 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" << 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" << 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]" << true
<< "-fsanitize-coverage=[func|bb|edge],[trace-pc-guard|trace-pc],["
"control-flow]";
}
Expand Down
11 changes: 5 additions & 6 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6561,7 +6561,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) << true
<< "-fvisibility-global-new-delete=force-hidden";
}

Expand Down Expand Up @@ -7288,11 +7288,10 @@ 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, with no effect.
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) << false;

// -fsized-deallocation is off by default, as it is an ABI-breaking change for
// most platforms.
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Frontend/InitPreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,8 +713,8 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
}
if (LangOpts.AlignedAllocation && !LangOpts.AlignedAllocationUnavailable)
Builder.defineMacro("__cpp_aligned_new", "201606L");
if (LangOpts.RelaxedTemplateTemplateArgs)
Builder.defineMacro("__cpp_template_template_args", "201611L");

Builder.defineMacro("__cpp_template_template_args", "201611L");

// C++20 features.
if (LangOpts.CPlusPlus20) {
Expand Down
88 changes: 41 additions & 47 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8343,58 +8343,52 @@ 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
// template parameters matches the corresponding template parameter in
// the template-parameter-list of P.
if (TemplateParameterListsAreEqual(
Template->getTemplateParameters(), Params, false,
TPL_TemplateTemplateArgumentMatch, Arg.getLocation()) &&
// If the argument has no associated constraints, then the parameter is
// definitely at least as specialized as the argument.
// Otherwise - we need a more thorough check.
!Template->hasAssociatedConstraints())
return false;
// Quick check for the common case:
// If P contains a parameter pack, then A [...] matches P if each of A's
// template parameters matches the corresponding template parameter in
// the template-parameter-list of P.
if (TemplateParameterListsAreEqual(Template->getTemplateParameters(), Params,
false, TPL_TemplateTemplateArgumentMatch,
Arg.getLocation()) &&
// If the argument has no associated constraints, then the parameter is
// definitely at least as specialized as the argument.
// Otherwise - we need a more thorough check.
!Template->hasAssociatedConstraints())
return false;

if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
Arg.getLocation())) {
// P2113
// C++20[temp.func.order]p2
// [...] If both deductions succeed, the partial ordering selects the
// more constrained template (if one exists) as determined below.
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
Params->getAssociatedConstraints(ParamsAC);
// C++2a[temp.arg.template]p3
// [...] In this comparison, if P is unconstrained, the constraints on A
// are not considered.
if (ParamsAC.empty())
return false;
if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
Arg.getLocation())) {
// P2113
// C++20[temp.func.order]p2
// [...] If both deductions succeed, the partial ordering selects the
// more constrained template (if one exists) as determined below.
SmallVector<const Expr *, 3> ParamsAC, TemplateAC;
Params->getAssociatedConstraints(ParamsAC);
// C++2a[temp.arg.template]p3
// [...] In this comparison, if P is unconstrained, the constraints on A
// are not considered.
if (ParamsAC.empty())
return false;

Template->getAssociatedConstraints(TemplateAC);
Template->getAssociatedConstraints(TemplateAC);

bool IsParamAtLeastAsConstrained;
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
IsParamAtLeastAsConstrained))
return true;
if (!IsParamAtLeastAsConstrained) {
Diag(Arg.getLocation(),
diag::err_template_template_parameter_not_at_least_as_constrained)
<< Template << Param << Arg.getSourceRange();
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
Diag(Template->getLocation(), diag::note_entity_declared_at)
<< Template;
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
TemplateAC);
return true;
}
return false;
bool IsParamAtLeastAsConstrained;
if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC,
IsParamAtLeastAsConstrained))
return true;
if (!IsParamAtLeastAsConstrained) {
Diag(Arg.getLocation(),
diag::err_template_template_parameter_not_at_least_as_constrained)
<< Template << Param << Arg.getSourceRange();
Diag(Param->getLocation(), diag::note_entity_declared_at) << Param;
Diag(Template->getLocation(), diag::note_entity_declared_at) << Template;
MaybeEmitAmbiguousAtomicConstraintsDiagnostic(Param, ParamsAC, Template,
TemplateAC);
return true;
}
// FIXME: Produce better diagnostics for deduction failures.
return false;
}
// FIXME: Produce better diagnostics for deduction failures.

return !TemplateParameterListsAreEqual(Template->getTemplateParameters(),
Params,
Expand Down
99 changes: 93 additions & 6 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,10 +507,62 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument(
S, TemplateParams, NTTP, DeducedTemplateArgument(New), T, Info, Deduced);
}

static NamedDecl *DeduceTemplateArguments(Sema &S, NamedDecl *A,
TemplateArgument Default) {
switch (A->getKind()) {
case Decl::TemplateTypeParm: {
auto *T = cast<TemplateTypeParmDecl>(A);
// FIXME: DefaultArgument can't represent a pack.
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: DefaultArgument can't represent a pack.
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, TemplateArgumentLoc(Default, TemplateArgumentLocInfo()));
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 +571,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] =
DeduceTemplateArguments(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 +688,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 +715,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 @@ -2320,7 +2406,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]
5 changes: 1 addition & 4 deletions clang/test/Lexer/cxx-features.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
// 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 -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,9 +230,7 @@
#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) \
: check(template_template_args, 0, 0, 0, 0, 0, 0, 0)
#if check(template_template_args, 201611, 201611, 201611, 201611, 201611, 201611, 201611)
#error "wrong value for __cpp_template_template_args"
#endif

Expand Down
Loading

0 comments on commit 873043f

Please sign in to comment.