r290792 - [c++17] Implement P0522R0 as written. This allows a template template argument

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Sat Dec 31 13:41:24 PST 2016


Author: rsmith
Date: Sat Dec 31 15:41:23 2016
New Revision: 290792

URL: http://llvm.org/viewvc/llvm-project?rev=290792&view=rev
Log:
[c++17] Implement P0522R0 as written. This allows a template template argument
to be specified for a template template parameter whenever the parameter is at
least as specialized as the argument (when there's an obvious and correct
mapping from uses of the parameter to uses of the argument). For example, a
template with more parameters can be passed to a template template parameter
with fewer, if those trailing parameters have default arguments.

This is disabled by default, despite being a DR resolution, as it's fairly
broken in its current state: there are no partial ordering rules to cope with
template template parameters that have different parameter lists, meaning that
code that attempts to decompose template-ids based on arity can hit unavoidable
ambiguity issues.

The diagnostics produced on a non-matching argument are also pretty bad right
now, but I aim to improve them in a subsequent commit.

Added:
    cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp
Modified:
    cfe/trunk/include/clang/Basic/LangOptions.def
    cfe/trunk/include/clang/Driver/Options.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Driver/Tools.cpp
    cfe/trunk/lib/Frontend/CompilerInvocation.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/SemaTemplate/temp_arg_template.cpp
    cfe/trunk/www/cxx_status.html

Modified: cfe/trunk/include/clang/Basic/LangOptions.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/LangOptions.def?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/LangOptions.def (original)
+++ cfe/trunk/include/clang/Basic/LangOptions.def Sat Dec 31 15:41:23 2016
@@ -134,6 +134,7 @@ LANGOPT(NoBuiltin         , 1, 0, "disab
 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")

Modified: cfe/trunk/include/clang/Driver/Options.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/Options.td (original)
+++ cfe/trunk/include/clang/Driver/Options.td Sat Dec 31 15:41:23 2016
@@ -1088,6 +1088,11 @@ def fapplication_extension : Flag<["-"],
   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>;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Sat Dec 31 15:41:23 2016
@@ -6719,6 +6719,9 @@ public:
   bool isMoreSpecializedThanPrimary(VarTemplatePartialSpecializationDecl *T,
                                     sema::TemplateDeductionInfo &Info);
 
+  bool isTemplateTemplateParameterAtLeastAsSpecializedAs(
+      TemplateParameterList *P, TemplateDecl *AArg, SourceLocation Loc);
+
   void MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs,
                                   bool OnlyDeduced,
                                   unsigned Depth,

Modified: cfe/trunk/lib/Driver/Tools.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Tools.cpp?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Tools.cpp (original)
+++ cfe/trunk/lib/Driver/Tools.cpp Sat Dec 31 15:41:23 2016
@@ -6020,6 +6020,13 @@ void Clang::ConstructJob(Compilation &C,
                     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,

Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Sat Dec 31 15:41:23 2016
@@ -1960,6 +1960,8 @@ static void ParseLangArgs(LangOptions &O
   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,

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Sat Dec 31 15:41:23 2016
@@ -5585,6 +5585,10 @@ ExprResult Sema::CheckTemplateArgument(N
   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.
 ///
@@ -5601,6 +5605,9 @@ bool Sema::CheckTemplateArgument(Templat
     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
@@ -5628,6 +5635,25 @@ bool Sema::CheckTemplateArgument(Templat
   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,
@@ -5839,7 +5865,7 @@ static bool MatchTemplateParameterKind(S
     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.

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Sat Dec 31 15:41:23 2016
@@ -1898,11 +1898,11 @@ DeduceTemplateArguments(Sema &S, Templat
         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
@@ -1965,7 +1965,8 @@ DeduceTemplateArguments(Sema &S,
                         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.
@@ -4581,13 +4582,13 @@ UnresolvedSetIterator Sema::getMostSpeci
 /// 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
@@ -4729,6 +4730,72 @@ bool Sema::isMoreSpecializedThanPrimary(
   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,

Modified: cfe/trunk/test/SemaTemplate/temp_arg_template.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_template.cpp?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_template.cpp (original)
+++ cfe/trunk/test/SemaTemplate/temp_arg_template.cpp Sat Dec 31 15:41:23 2016
@@ -6,11 +6,12 @@ template<template<typename T> class X> s
 
 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;
@@ -27,6 +28,7 @@ A<TooMany> *a5; // expected-error{{templ
 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);
 

Added: cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp?rev=290792&view=auto
==============================================================================
--- cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp (added)
+++ cfe/trunk/test/SemaTemplate/temp_arg_template_cxx1z.cpp Sat Dec 31 15:41:23 2016
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z -frelaxed-template-template-args %s
+
+// expected-note at 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}}
+}

Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=290792&r1=290791&r2=290792&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Sat Dec 31 15:41:23 2016
@@ -733,7 +733,7 @@ as the draft C++1z standard evolves.
     <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>
@@ -763,8 +763,12 @@ left to right in the callee. As a result
 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>




More information about the cfe-commits mailing list