Canonicalize implicit deduction guide parameter types when forming a deduction
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.
llvm-svn: 295016
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index e86f287..fd66c79 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1585,12 +1585,7 @@
// -- 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 @@
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;
}
diff --git a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp
index 6b45a8b..011130d 100644
--- a/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp
+++ b/clang/test/SemaCXX/cxx1z-class-template-argument-deduction.cpp
@@ -136,4 +136,17 @@
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};
}