]> granicus.if.org Git - clang/commitdiff
Canonicalize implicit deduction guide parameter types when forming a deduction
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 14 Feb 2017 01:49:59 +0000 (01:49 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 14 Feb 2017 01:49:59 +0000 (01:49 +0000)
guide from a constructor.

The purpose of this change is to avoid triggering instantiation of the class
when substituting back into the deduction guide if it uses a typedef member.
We will still instantiate the class if the constructor (explicitly or
implicitly, directly or indirectly) uses the current instantiation in a way
that we can't canonicalize out, but that seems unavoidable.

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

lib/Sema/SemaTemplate.cpp
test/SemaCXX/cxx1z-class-template-argument-deduction.cpp

index e86f287e0c592e0e8cdd21542e607b98717c4cbc..fd66c798654abe8be40c39471d04db5ebea5a4f2 100644 (file)
@@ -1585,12 +1585,7 @@ private:
 
     //    -- The types of the function parameters are those of the constructor.
     for (auto *OldParam : TL.getParams()) {
-      // If we're transforming a non-template constructor, just reuse its
-      // parameters as the parameters of the deduction guide. Otherwise, we
-      // need to transform their references to constructor template parameters.
-      ParmVarDecl *NewParam = Args.getNumLevels()
-                                  ? transformFunctionTypeParam(OldParam, Args)
-                                  : OldParam;
+      ParmVarDecl *NewParam = transformFunctionTypeParam(OldParam, Args);
       if (!NewParam)
         return QualType();
       ParamTypes.push_back(NewParam->getType());
@@ -1636,16 +1631,31 @@ private:
   transformFunctionTypeParam(ParmVarDecl *OldParam,
                              MultiLevelTemplateArgumentList &Args) {
     TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
-    TypeSourceInfo *NewDI = SemaRef.SubstType(
-        OldDI, Args, OldParam->getLocation(), OldParam->getDeclName());
+    TypeSourceInfo *NewDI =
+        Args.getNumLevels()
+            ? SemaRef.SubstType(OldDI, Args, OldParam->getLocation(),
+                                OldParam->getDeclName())
+            : OldDI;
     if (!NewDI)
       return nullptr;
 
+    // Canonicalize the type. This (for instance) replaces references to
+    // typedef members of the current instantiations with the definitions of
+    // those typedefs, avoiding triggering instantiation of the deduced type
+    // during deduction.
+    // FIXME: It would be preferable to retain type sugar and source
+    // information here (and handle this in substitution instead).
+    NewDI = SemaRef.Context.getTrivialTypeSourceInfo(
+        SemaRef.Context.getCanonicalType(NewDI->getType()),
+        OldParam->getLocation());
+
     // Resolving a wording defect, we also inherit default arguments from the
     // constructor.
     ExprResult NewDefArg;
     if (OldParam->hasDefaultArg()) {
-      NewDefArg = SemaRef.SubstExpr(OldParam->getDefaultArg(), Args);
+      NewDefArg = Args.getNumLevels()
+                      ? SemaRef.SubstExpr(OldParam->getDefaultArg(), Args)
+                      : OldParam->getDefaultArg();
       if (NewDefArg.isInvalid())
         return nullptr;
     }
index 6b45a8bb2ae0fb9a5ba44324385ff20d6bc30f6c..011130d0e77780c172447e8cbc4c21885831e8d0 100644 (file)
@@ -136,4 +136,17 @@ namespace look_into_current_instantiation {
     B(typename X::type); // expected-note {{couldn't infer template argument 'T'}}
   };
   B b = 0; // expected-error {{no viable}}
+
+  // We should have a substitution failure in the immediate context of
+  // deduction when using the C(T, U) constructor (probably; core wording
+  // unclear).
+  template<typename T> struct C {
+    using U = typename T::type;
+    C(T, U);
+  };
+
+  struct R { R(int); typedef R type; };
+  C(...) -> C<R>;
+
+  C c = {1, 2};
 }