]> granicus.if.org Git - clang/commitdiff
[c++17] Implement P0522R0 as written. This allows a template template argument
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 31 Dec 2016 21:41:23 +0000 (21:41 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 31 Dec 2016 21:41:23 +0000 (21:41 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@290792 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/LangOptions.def
include/clang/Driver/Options.td
include/clang/Sema/Sema.h
lib/Driver/Tools.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateDeduction.cpp
test/SemaTemplate/temp_arg_template.cpp
test/SemaTemplate/temp_arg_template_cxx1z.cpp [new file with mode: 0644]
www/cxx_status.html

index e65811b361152bc12579c232f0011358e6589c17..e10d5c474dc8213c67be18e3b6c695c286cbc937 100644 (file)
@@ -134,6 +134,7 @@ LANGOPT(NoBuiltin         , 1, 0, "disable builtin functions")
 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")
index 09b15285f8b37b9f9e93490ceda2ea6deb83b3c2..1f1222e106363bc41c0eebf687ef7c874cb18468 100644 (file)
@@ -1088,6 +1088,11 @@ def fapplication_extension : Flag<["-"], "fapplication-extension">,
   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>;
index d012ebdab2ff5a916a6d94fd653b14c4940cea8c..82caaeb24ae72ff2d579ebc58ca87d26831d0c98 100644 (file)
@@ -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,
index 8ed12d72b40891ab4618cf503debe67183c05b88..2a367bb29aa5fe4fc81a9e85d9db3bd034b26a56 100644 (file)
@@ -6020,6 +6020,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
                     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,
index ae4417c93c28c44c1878d5b4e5d989fb72a00ec0..a0682e26e7025c5b2e5d5a1354e1e1d85f37feb0 100644 (file)
@@ -1960,6 +1960,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
   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,
index ec2f598814541a9c5d18de59d1448ab3dcf6ff74..facc5d1b375bd70d4b5294c0ae43d6da8b4d514c 100644 (file)
@@ -5585,6 +5585,10 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
   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(TemplateTemplateParmDecl *Param,
     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(TemplateTemplateParmDecl *Param,
   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(Sema &S, NamedDecl *New, NamedDecl *Old,
     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.
index df796072f597798558844226b41e4e70225be702..68b9853bdf675b80f8499742bd62a75e1f49077b 100644 (file)
@@ -1898,11 +1898,11 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
         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::getMostSpecialized(
 /// 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,
index 6d93f1504a46990497126bd6c6aa6f0822ad84b7..67cde53c928b384d1c7c0f863c8791a8a18bc89e 100644 (file)
@@ -6,11 +6,12 @@ template<template<typename T> class X> struct A; // expected-note 2{{previous te
 
 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{{template template argument has different temp
 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);
 
diff --git a/test/SemaTemplate/temp_arg_template_cxx1z.cpp b/test/SemaTemplate/temp_arg_template_cxx1z.cpp
new file mode 100644 (file)
index 0000000..b6b283b
--- /dev/null
@@ -0,0 +1,102 @@
+// 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}}
+}
index 4a9236a01460d2e67eb5edb30b49e36ebbc4aa96..675cc6f7a82bd76c9cf451b0bc750f662c1afe70 100644 (file)
@@ -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, function parameters in calls to
 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>