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
This commit is contained in:
Richard Smith 2017-02-14 01:49:59 +00:00
parent b2bca7e309
commit c27b3d7623
2 changed files with 32 additions and 9 deletions

View File

@ -1585,12 +1585,7 @@ private:
// -- The types of the function parameters are those of the constructor. // -- The types of the function parameters are those of the constructor.
for (auto *OldParam : TL.getParams()) { for (auto *OldParam : TL.getParams()) {
// If we're transforming a non-template constructor, just reuse its ParmVarDecl *NewParam = transformFunctionTypeParam(OldParam, Args);
// 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;
if (!NewParam) if (!NewParam)
return QualType(); return QualType();
ParamTypes.push_back(NewParam->getType()); ParamTypes.push_back(NewParam->getType());
@ -1636,16 +1631,31 @@ private:
transformFunctionTypeParam(ParmVarDecl *OldParam, transformFunctionTypeParam(ParmVarDecl *OldParam,
MultiLevelTemplateArgumentList &Args) { MultiLevelTemplateArgumentList &Args) {
TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo(); TypeSourceInfo *OldDI = OldParam->getTypeSourceInfo();
TypeSourceInfo *NewDI = SemaRef.SubstType( TypeSourceInfo *NewDI =
OldDI, Args, OldParam->getLocation(), OldParam->getDeclName()); Args.getNumLevels()
? SemaRef.SubstType(OldDI, Args, OldParam->getLocation(),
OldParam->getDeclName())
: OldDI;
if (!NewDI) if (!NewDI)
return nullptr; 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 // Resolving a wording defect, we also inherit default arguments from the
// constructor. // constructor.
ExprResult NewDefArg; ExprResult NewDefArg;
if (OldParam->hasDefaultArg()) { if (OldParam->hasDefaultArg()) {
NewDefArg = SemaRef.SubstExpr(OldParam->getDefaultArg(), Args); NewDefArg = Args.getNumLevels()
? SemaRef.SubstExpr(OldParam->getDefaultArg(), Args)
: OldParam->getDefaultArg();
if (NewDefArg.isInvalid()) if (NewDefArg.isInvalid())
return nullptr; return nullptr;
} }

View File

@ -136,4 +136,17 @@ namespace look_into_current_instantiation {
B(typename X::type); // expected-note {{couldn't infer template argument 'T'}} B(typename X::type); // expected-note {{couldn't infer template argument 'T'}}
}; };
B b = 0; // expected-error {{no viable}} 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};
} }