diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index e65811b36115..e10d5c474dc8 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -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")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 09b15285f8b3..1f1222e10636 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -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>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index d012ebdab2ff..82caaeb24ae7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -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,
diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp
index 8ed12d72b408..2a367bb29aa5 100644
--- a/clang/lib/Driver/Tools.cpp
+++ b/clang/lib/Driver/Tools.cpp
@@ -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,
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index ae4417c93c28..a0682e26e702 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -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,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index ec2f59881454..facc5d1b375b 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -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.
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index df796072f597..68b9853bdf67 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -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,
diff --git a/clang/test/SemaTemplate/temp_arg_template.cpp b/clang/test/SemaTemplate/temp_arg_template.cpp
index 6d93f1504a46..67cde53c928b 100644
--- a/clang/test/SemaTemplate/temp_arg_template.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template.cpp
@@ -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/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
new file mode 100644
index 000000000000..b6b283b53c6b
--- /dev/null
+++ b/clang/test/SemaTemplate/temp_arg_template_cxx1z.cpp
@@ -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}}
+}
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 4a9236a01460..675cc6f7a82b 100644
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -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>