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.
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;
}

View 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};
}