diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -1798,8 +1798,10 @@
 
     unsigned : NumTypeBits;
 
+    unsigned hasNonCanonicalUnderlyingType : 1;
+
     // The index of the template parameter this substitution represents.
-    unsigned Index;
+    unsigned Index : 16;
   };
 
   class SubstTemplateTypeParmPackTypeBitfields {
@@ -4981,8 +4983,12 @@
 /// been replaced with these.  They are used solely to record that a
 /// type was originally written as a template type parameter;
 /// therefore they are never canonical.
-class SubstTemplateTypeParmType : public Type, public llvm::FoldingSetNode {
+class SubstTemplateTypeParmType final
+    : public Type,
+      public llvm::FoldingSetNode,
+      private llvm::TrailingObjects<SubstTemplateTypeParmType, QualType> {
   friend class ASTContext;
+  friend class llvm::TrailingObjects<SubstTemplateTypeParmType, QualType>;
 
   Decl *ReplacedDecl;
 
@@ -4992,7 +4998,11 @@
 public:
   /// Gets the type that was substituted for the template
   /// parameter.
-  QualType getReplacementType() const { return getCanonicalTypeInternal(); }
+  QualType getReplacementType() const {
+    return SubstTemplateTypeParmTypeBits.hasNonCanonicalUnderlyingType
+               ? *getTrailingObjects<QualType>()
+               : getCanonicalTypeInternal();
+  }
 
   /// Gets the templated entity that was substituted.
   Decl *getReplacedDecl() const { return ReplacedDecl; }
@@ -5011,7 +5021,7 @@
   static void Profile(llvm::FoldingSetNodeID &ID, QualType Replacement,
                       const Decl *ReplacedDecl, unsigned Index) {
     ID.AddPointer(ReplacedDecl);
-    ID.AddPointer(Replacement.getAsOpaquePtr());
+    Replacement.Profile(ID);
     ID.AddInteger(Index);
   }
 
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -741,7 +741,7 @@
   // The call to getCanonicalType here existed in ASTReader.cpp, too.
   def : Creator<[{
     return ctx.getSubstTemplateTypeParmType(
-        ctx.getCanonicalType(replacementType), replacedDecl, Index);
+        replacementType, replacedDecl, Index);
   }]>;
 }
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -4752,9 +4752,6 @@
 QualType ASTContext::getSubstTemplateTypeParmType(QualType Replacement,
                                                   Decl *ReplacedDecl,
                                                   unsigned Index) const {
-  assert(Replacement.isCanonical()
-         && "replacement types must always be canonical");
-
   llvm::FoldingSetNodeID ID;
   SubstTemplateTypeParmType::Profile(ID, Replacement, ReplacedDecl, Index);
   void *InsertPos = nullptr;
@@ -4762,8 +4759,11 @@
       SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos);
 
   if (!SubstParm) {
-    SubstParm = new (*this, TypeAlignment)
-        SubstTemplateTypeParmType(Replacement, ReplacedDecl, Index);
+    void *Mem = Allocate(SubstTemplateTypeParmType::totalSizeToAlloc<QualType>(
+                             !Replacement.isCanonical()),
+                         TypeAlignment);
+    SubstParm =
+        new (Mem) SubstTemplateTypeParmType(Replacement, ReplacedDecl, Index);
     Types.push_back(SubstParm);
     SubstTemplateTypeParmTypes.InsertNode(SubstParm, InsertPos);
   }
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3718,6 +3718,11 @@
     : Type(SubstTemplateTypeParm, Replacement.getCanonicalType(),
            Replacement->getDependence()),
       ReplacedDecl(ReplacedDecl) {
+  SubstTemplateTypeParmTypeBits.hasNonCanonicalUnderlyingType =
+      Replacement != getCanonicalTypeInternal();
+  if (SubstTemplateTypeParmTypeBits.hasNonCanonicalUnderlyingType)
+    *getTrailingObjects<QualType>() = Replacement;
+
   SubstTemplateTypeParmTypeBits.Index = Index;
   assert(ReplacedDecl != nullptr);
   assert(getReplacedParameter() != nullptr);