LANGOPT(NoMathBuiltin , 1, 0, "disable math builtin functions")
LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly")
LANGOPT(CoroutinesTS , 1, 0, "C++ coroutines TS")
+LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of tempalte template arguments")
BENIGN_LANGOPT(ThreadsafeStatics , 1, 1, "thread-safe static initializers")
LANGOPT(POSIXThreads , 1, 0, "POSIX thread support")
HelpText<"Restrict code to those available for App Extensions">;
def fno_application_extension : Flag<["-"], "fno-application-extension">,
Group<f_Group>;
+def frelaxed_template_template_args : Flag<["-"], "frelaxed-template-template-args">,
+ Flags<[CC1Option]>, HelpText<"Enable C++17 relaxed template template argument matching">,
+ Group<f_Group>;
+def fno_relaxed_template_template_args : Flag<["-"], "fno-relaxed-template-template-args">,
+ Group<f_Group>;
def fsized_deallocation : Flag<["-"], "fsized-deallocation">, Flags<[CC1Option]>,
HelpText<"Enable C++14 sized global deallocation functions">, Group<f_Group>;
def fno_sized_deallocation: Flag<["-"], "fno-sized-deallocation">, Group<f_Group>;
bool isMoreSpecializedThanPrimary(VarTemplatePartialSpecializationDecl *T,
sema::TemplateDeductionInfo &Info);
+ bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
+ TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc);
+
void MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs,
bool OnlyDeduced,
unsigned Depth,
options::OPT_fno_assume_sane_operator_new))
CmdArgs.push_back("-fno-assume-sane-operator-new");
+ // -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.
+ if (Args.hasFlag(options::OPT_frelaxed_template_template_args,
+ options::OPT_fno_relaxed_template_template_args, false))
+ CmdArgs.push_back("-frelaxed-template-template-args");
+
// -fsized-deallocation is off by default, as it is an ABI-breaking change for
// most platforms.
if (Args.hasFlag(options::OPT_fsized_deallocation,
if (!Opts.NoBuiltin)
getAllNoBuiltinFuncValues(Args, Opts.NoBuiltinFuncs);
Opts.NoMathBuiltin = Args.hasArg(OPT_fno_math_builtin);
+ Opts.RelaxedTemplateTemplateArgs =
+ Args.hasArg(OPT_frelaxed_template_template_args);
Opts.SizedDeallocation = Args.hasArg(OPT_fsized_deallocation);
Opts.AlignedAllocation =
Args.hasFlag(OPT_faligned_allocation, OPT_fno_aligned_allocation,
return Arg;
}
+static void DiagnoseTemplateParameterListArityMismatch(
+ Sema &S, TemplateParameterList *New, TemplateParameterList *Old,
+ Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc);
+
/// \brief Check a template argument against its corresponding
/// template template parameter.
///
return false;
}
+ if (Template->isInvalidDecl())
+ return true;
+
// C++0x [temp.arg.template]p1:
// A template-argument for a template template-parameter shall be
// the name of a class template or an alias template, expressed as an
if (Param->isExpandedParameterPack())
Params = Param->getExpansionTemplateParameters(ArgumentPackIndex);
+ // 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.
+ 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()))
+ return false;
+
+ if (isTemplateTemplateParameterAtLeastAsSpecializedAs(Params, Template,
+ Arg.getLocation()))
+ return false;
+ // FIXME: Produce better diagnostics for deduction failures.
+ }
+
return !TemplateParameterListsAreEqual(Template->getTemplateParameters(),
Params,
true,
return false;
}
- // Check that both are parameter packs are neither are parameter packs.
+ // Check that both are parameter packs or neither are parameter packs.
// However, if we are matching a template template argument to a
// template template parameter, the template template parameter can have
// a parameter pack where the template template argument does not.
return NumberOfArgumentsMustMatch ? Sema::TDK_TooFewArguments
: Sema::TDK_Success;
- if (Args[ArgIdx].isPackExpansion()) {
- // FIXME: We follow the logic of C++0x [temp.deduct.type]p22 here,
- // but applied to pack expansions that are template arguments.
+ // C++1z [temp.deduct.type]p9:
+ // During partial ordering, if Ai was originally a pack expansion [and]
+ // Pi is not a pack expansion, template argument deduction fails.
+ if (Args[ArgIdx].isPackExpansion())
return Sema::TDK_MiscellaneousDeductionFailure;
- }
// Perform deduction for this Pi/Ai pair.
if (Sema::TemplateDeductionResult Result
TemplateDeductionInfo &Info,
SmallVectorImpl<DeducedTemplateArgument> &Deduced) {
return DeduceTemplateArguments(S, TemplateParams, ParamList.asArray(),
- ArgList.asArray(), Info, Deduced, false);
+ ArgList.asArray(), Info, Deduced,
+ /*NumberOfArgumentsMustMatch*/false);
}
/// \brief Determine whether two template arguments are the same.
/// Determine whether one partial specialization, P1, is at least as
/// specialized than another, P2.
///
-/// \tparam PartialSpecializationDecl The kind of P2, which must be a
-/// {Class,Var}Template{PartialSpecialization,}Decl.
+/// \tparam TemplateLikeDecl The kind of P2, which must be a
+/// TemplateDecl or {Class,Var}TemplatePartialSpecializationDecl.
/// \param T1 The injected-class-name of P1 (faked for a variable template).
/// \param T2 The injected-class-name of P2 (faked for a variable template).
-template<typename PartialSpecializationDecl>
+template<typename TemplateLikeDecl>
static bool isAtLeastAsSpecializedAs(Sema &S, QualType T1, QualType T2,
- PartialSpecializationDecl *P2,
+ TemplateLikeDecl *P2,
TemplateDeductionInfo &Info) {
// C++ [temp.class.order]p1:
// For two class template partial specializations, the first is at least as
return true;
}
+bool Sema::isTemplateTemplateParameterAtLeastAsSpecializedAs(
+ TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc) {
+ // 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
+ // function templates...
+
+ // Rather than synthesize function templates, we merely perform the
+ // equivalent partial ordering by performing deduction directly on
+ // the template parameter lists of the template template parameters.
+ //
+ // Given an invented class template X with the template parameter list of
+ // A (including default arguments):
+ TemplateName X = Context.getCanonicalTemplateName(TemplateName(AArg));
+ TemplateParameterList *A = AArg->getTemplateParameters();
+
+ // - Each function template has a single function parameter whose type is
+ // a specialization of X with template arguments corresponding to the
+ // template parameters from the respective function template
+ SmallVector<TemplateArgument, 8> AArgs;
+ Context.getInjectedTemplateArgs(A, AArgs);
+
+ // Check P's arguments against A's parameter list. This will fill in default
+ // template arguments as needed. AArgs are already correct by construction.
+ // We can't just use CheckTemplateIdType because that will expand alias
+ // templates.
+ SmallVector<TemplateArgument, 4> PArgs;
+ {
+ SFINAETrap Trap(*this);
+
+ Context.getInjectedTemplateArgs(P, PArgs);
+ TemplateArgumentListInfo PArgList(P->getLAngleLoc(), P->getRAngleLoc());
+ for (unsigned I = 0, N = P->size(); I != N; ++I) {
+ // Unwrap packs that getInjectedTemplateArgs wrapped around pack
+ // expansions, to form an "as written" argument list.
+ TemplateArgument Arg = PArgs[I];
+ if (Arg.getKind() == TemplateArgument::Pack) {
+ assert(Arg.pack_size() == 1 && Arg.pack_begin()->isPackExpansion());
+ Arg = *Arg.pack_begin();
+ }
+ PArgList.addArgument(getTrivialTemplateArgumentLoc(
+ Arg, QualType(), P->getParam(I)->getLocation()));
+ }
+ PArgs.clear();
+
+ // C++1z [temp.arg.template]p3:
+ // If the rewrite produces an invalid type, then P is not at least as
+ // specialized as A.
+ if (CheckTemplateArgumentList(AArg, Loc, PArgList, false, PArgs) ||
+ Trap.hasErrorOccurred())
+ return false;
+ }
+
+ QualType AType = Context.getTemplateSpecializationType(X, AArgs);
+ QualType PType = Context.getTemplateSpecializationType(X, PArgs);
+
+ SmallVector<DeducedTemplateArgument, 4> Deduced;
+ Deduced.resize(A->size());
+
+ // ... the function template corresponding to P is at least as specialized
+ // as the function template corresponding to A according to the partial
+ // ordering rules for function templates.
+ TemplateDeductionInfo Info(Loc, A->getDepth());
+ return isAtLeastAsSpecializedAs(*this, PType, AType, AArg, Info);
+}
+
static void
MarkUsedTemplateParameters(ASTContext &Ctx,
const TemplateArgument &TemplateArg,
template<template<typename T, int I> class X> struct B; // expected-note{{previous template template parameter is here}}
-template<template<int I> class X> struct C; // expected-note{{previous non-type template parameter with type 'int' is here}}
+template<template<int I> class X> struct C; // expected-note 2{{previous non-type template parameter with type 'int' is here}}
template<class> struct X; // expected-note{{too few template parameters in template template argument}}
template<int N> struct Y; // expected-note{{template parameter has a different kind in template argument}}
template<long N> struct Ylong; // expected-note{{template non-type parameter has a different type 'long' in template argument}}
+template<const int &N> struct Yref; // expected-note{{template non-type parameter has a different type 'const int &' in template argument}}
namespace N {
template<class> struct Z;
B<X> *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
C<Y> *a7;
C<Ylong> *a8; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
+C<Yref> *a9; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
template<typename T> void f(int);
--- /dev/null
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z -frelaxed-template-template-args %s
+
+// expected-note@temp_arg_template_cxx1z.cpp:* 1+{{}}
+
+template<template<int> typename> struct Ti;
+template<template<int...> typename> struct TPi;
+template<template<int, int...> typename> struct TiPi;
+template<template<int..., int...> typename> struct TPiPi; // FIXME: Why is this not ill-formed?
+
+template<typename T, template<T> typename> struct tT0;
+template<template<typename T, T> typename> struct Tt0;
+
+template<template<typename> typename> struct Tt;
+template<template<typename, typename...> typename> struct TtPt;
+
+template<int> struct i;
+template<int, int = 0> struct iDi;
+template<int, int> struct ii;
+template<int...> struct Pi;
+template<int, int, int...> struct iiPi;
+
+template<int, typename = int> struct iDt;
+template<int, typename> struct it;
+
+template<typename T, T v> struct t0;
+
+template<typename...> struct Pt;
+
+namespace IntParam {
+ using ok = Pt<Ti<i>,
+ Ti<iDi>,
+ Ti<Pi>,
+ Ti<iDt>>;
+ using err1 = Ti<ii>; // expected-error {{different template parameters}}
+ using err2 = Ti<iiPi>; // expected-error {{different template parameters}}
+ using err3 = Ti<t0>; // expected-error {{different template parameters}}
+ using err4 = Ti<it>; // expected-error {{different template parameters}}
+}
+
+// These are accepted by the backwards-compatibility "parameter pack in
+// parameter matches any number of parameters in arguments" rule.
+namespace IntPackParam {
+ using ok = TPi<Pi>;
+ using ok_compat = Pt<TPi<i>, TPi<iDi>, TPi<ii>, TPi<iiPi>>;
+ using err1 = TPi<t0>; // expected-error {{different template parameters}}
+ using err2 = TPi<iDt>; // expected-error {{different template parameters}}
+ using err3 = TPi<it>; // expected-error {{different template parameters}}
+}
+
+namespace IntAndPackParam {
+ using ok = TiPi<Pi>;
+ using ok_compat = Pt<TiPi<ii>, TiPi<iDi>, TiPi<iiPi>>;
+ using err = TiPi<iDi>;
+}
+
+namespace DependentType {
+ using ok = Pt<tT0<int, i>, tT0<int, iDi>>;
+ using err1 = tT0<int, ii>; // expected-error {{different template parameters}}
+ using err2 = tT0<short, i>; // FIXME: should this be OK?
+ using err2a = tT0<long long, i>; // FIXME: should this be OK (if long long is larger than int)?
+ using err2b = tT0<void*, i>; // expected-error {{different template parameters}}
+ using err3 = tT0<short, t0>; // expected-error {{different template parameters}}
+
+ using ok2 = Tt0<t0>;
+ using err4 = Tt0<it>; // expected-error {{different template parameters}}
+}
+
+namespace Auto {
+ template<template<int> typename T> struct TInt {};
+ template<template<int*> typename T> struct TIntPtr {};
+ template<template<auto> typename T> struct TAuto {};
+ template<template<auto*> typename T> struct TAutoPtr {};
+ template<auto> struct Auto;
+ template<auto*> struct AutoPtr;
+ template<int> struct Int;
+ template<int*> struct IntPtr;
+
+ TInt<Auto> ia;
+ TInt<AutoPtr> iap; // FIXME: ill-formed
+ TInt<Int> ii;
+ TInt<IntPtr> iip; // expected-error {{different template parameters}}
+
+ TIntPtr<Auto> ipa;
+ TIntPtr<AutoPtr> ipap;
+ TIntPtr<Int> ipi; // expected-error {{different template parameters}}
+ TIntPtr<IntPtr> ipip;
+
+ TAuto<Auto> aa;
+ TAuto<AutoPtr> aap; // FIXME: ill-formed
+ TAuto<Int> ai; // FIXME: ill-formed
+ TAuto<IntPtr> aip; // FIXME: ill-formed
+
+ TAutoPtr<Auto> apa;
+ TAutoPtr<AutoPtr> apap;
+ TAutoPtr<Int> api; // FIXME: ill-formed
+ TAutoPtr<IntPtr> apip; // FIXME: ill-formed
+
+ int n;
+ template<auto A, decltype(A) B = &n> struct SubstFailure;
+ TInt<SubstFailure> isf; // expected-error {{different template parameters}}
+ TIntPtr<SubstFailure> ipsf; // expected-error {{different template parameters}}
+}
<tr>
<td>Matching template template parameters to compatible arguments</td>
<td><a href="http://wg21.link/p0522r0">P0522R0</a></td>
- <td class="none" align="center">No <a href="#p0522">(12)</a></td>
+ <td class="partial" align="center">Partial <a href="#p0522">(12)</a></td>
</tr>
<tr>
<td>Removing deprecated dynamic exception specifications</td>
functions using expression syntax are no longer guaranteed to be destroyed in
reverse construction order in that ABI.
</span><br>
-<span id="p0522">(12): This is the resolution to a Defect Report, so will be
-applied to all language versions.
+<span id="p0522">(12): Despite being the the resolution to a Defect Report, this
+feature is disabled by default in all language versions, and can be enabled
+explicitly with the flag <tt>-frelaxed-template-template-args</tt>. The change
+to the standard lacks a corresponding change for template partial ordering,
+resulting in ambiguity errors for reasonable and previously-valid code. This
+issue is expected to be rectified soon.
</span>
</p>
</details>