diff options
Diffstat (limited to 'src/dotty/tools/dotc/core/TypeApplications.scala')
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 222 |
1 files changed, 142 insertions, 80 deletions
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 428178b81..9411ed004 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -46,13 +46,12 @@ class TypeApplications(val self: Type) extends AnyVal { /** The type parameters of this type are: * For a ClassInfo type, the type parameters of its class. * For a typeref referring to a class, the type parameters of the class. - * For a typeref referring to an alias type, the type parameters of the aliased type. - * For a typeref referring to an abstract type with a HigherKindedXYZ bound, the - * type parameters of the HigherKinded class. + * For a typeref referring to an alias or abstract type, the type parameters of + * its right hand side or upper bound. * For a refinement type, the type parameters of its parent, unless there's a - * refinement with the same name. Inherited by all other type proxies. - * For an intersection type A & B, the type parameters of its left operand, A. - * Empty list for all other types. + * refinement with the same name. + * For any other non-singleton type proxy, the type parameters of its underlying type. + * For any other type, the empty list. */ final def typeParams(implicit ctx: Context): List[TypeSymbol] = /*>|>*/ track("typeParams") /*<|<*/ { self match { @@ -61,56 +60,50 @@ class TypeApplications(val self: Type) extends AnyVal { case tp: TypeRef => val tsym = tp.typeSymbol if (tsym.isClass) tsym.typeParams - else if (tsym.info.isAlias) tp.underlying.typeParams - else tp.info.bounds.hi match { - case AndType(hkBound, other) if defn.hkTraits contains hkBound.typeSymbol => - hkBound.typeSymbol.typeParams - case _ => - Nil - } + else tp.underlying.typeParams case tp: RefinedType => - tp.parent.typeParams filterNot (_.name == tp.refinedName) + val tparams = tp.parent.typeParams + tp.refinedInfo match { + case TypeBounds(lo, hi) if lo eq hi => tparams.filterNot(_.name == tp.refinedName) + case _ => tparams + } + case tp: SingletonType => + Nil case tp: TypeProxy => tp.underlying.typeParams - case tp: AndType => - tp.tp1.typeParams case _ => Nil } } + /** The type parameters of the underlying class. * This is like `typeParams`, except for 3 differences. * First, it does not adjust type parameters in refined types. I.e. type arguments * do not remove corresponding type parameters. * Second, it will return Nil for BoundTypes because we might get a NullPointer exception * on PolyParam#underlying otherwise (demonstrated by showClass test). - * Third, it won't return higher-kinded type parameters. + * Third, it won't return higher-kinded type parameters, i.e. the type parameters of + * an abstract type are always empty. */ - final def safeUnderlyingTypeParams(implicit ctx: Context): List[TypeSymbol] = { - def ifCompleted(sym: Symbol): Symbol = if (sym.isCompleted) sym else NoSymbol + final def rawTypeParams(implicit ctx: Context): List[TypeSymbol] = { self match { case tp: ClassInfo => tp.cls.typeParams case tp: TypeRef => val tsym = tp.typeSymbol if (tsym.isClass) tsym.typeParams - else if (tsym.isAliasType) tp.underlying.safeUnderlyingTypeParams + else if (tsym.isAliasType) tp.underlying.rawTypeParams else Nil - case tp: BoundType => + case _: BoundType | _: SingletonType => Nil case tp: TypeProxy => - tp.underlying.safeUnderlyingTypeParams - case tp: AndType => - tp.tp1.safeUnderlyingTypeParams + tp.underlying.rawTypeParams case _ => Nil } } - def uninstantiatedTypeParams(implicit ctx: Context): List[TypeSymbol] = - typeParams filter (tparam => self.member(tparam.name).symbol == tparam) - - /** If type `tp` is equal, aliased-to, or upperbounded-by a type of the form + /** If type `tp` is equal, aliased-to, or upperbounded-by a type of the form * `LambdaXYZ { ... }`, the class symbol of that type, otherwise NoSymbol. * @param forcing if set, might force completion. If not, never forces * but returns NoSymbol when it would have to otherwise. @@ -143,11 +136,10 @@ class TypeApplications(val self: Type) extends AnyVal { /** Is type `tp` a Lambda with all Arg$ fields fully instantiated? */ def isInstantiatedLambda(tp: Type)(implicit ctx: Context): Boolean = - tp.isSafeLambda && tp.typeParams.forall(_.name == tpnme.Apply) + tp.isSafeLambda && tp.typeParams.isEmpty /** Encode the type resulting from applying this type to given arguments */ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { - def matchParams(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match { case arg :: args1 => if (tparams.isEmpty) { @@ -155,7 +147,11 @@ class TypeApplications(val self: Type) extends AnyVal { println(s"precomplete decls = ${self.typeSymbol.decls.toList.map(_.denot).mkString("\n ")}") } val tparam = tparams.head - val tp1 = RefinedType(tp, tparam.name, arg.toBounds(tparam)) + val arg1 = + if ((tparam is HigherKinded) && !arg.isLambda && arg.typeParams.nonEmpty) + arg.EtaExpand + else arg + val tp1 = RefinedType(tp, tparam.name, arg1.toBounds(tparam)) matchParams(tp1, tparams.tail, args1) case nil => tp } @@ -174,8 +170,8 @@ class TypeApplications(val self: Type) extends AnyVal { val safeTypeParams = if (tsym.isClass || !tp.typeSymbol.isCompleting) original.typeParams else { - ctx.warning("encountered F-bounded higher-kinded type parameters; assuming they are invariant") - defn.hkTrait(args map alwaysZero).typeParams + ctx.warning(i"encountered F-bounded higher-kinded type parameters for $tsym; assuming they are invariant") + defn.lambdaTrait(args map alwaysZero).typeParams } matchParams(tp, safeTypeParams, args) } @@ -186,8 +182,6 @@ class TypeApplications(val self: Type) extends AnyVal { tp.refinedInfo) case tp: TypeProxy => instantiate(tp.underlying, original) - case AndType(l, r) => - l.appliedTo(args) & r case tp: PolyType => tp.instantiate(args) case ErrorType => @@ -195,7 +189,10 @@ class TypeApplications(val self: Type) extends AnyVal { } if (args.isEmpty || !canHaveTypeParams) self - else instantiate(self, self) + else { + val res = instantiate(self, self) + if (isInstantiatedLambda(res)) res.select(tpnme.Apply) else res + } } final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil) @@ -363,12 +360,11 @@ class TypeApplications(val self: Type) extends AnyVal { * * type T = RHS * - * It is required that `C` is a class and that every bound symbol in `boundSyms` appears - * as an argument in `targs`. If these requirements are not met an error is - * signalled by calling the parameter `error`. - * - * The rewriting replaces bound symbols by references to the - * parameters of class C. Example: + * There are two strategies how this is achieved. + + * 1st strategy: Applies if `C` is a class such that every bound symbol in `boundSyms` + * appears as an argument in `targs`, and in the same order. Then the rewriting replaces + * bound symbols by references to the parameters of class C. Example: * * Say we have: * @@ -380,48 +376,114 @@ class TypeApplications(val self: Type) extends AnyVal { * * type A = Triple { type T1 = (this.T2, this.T2); type T3 = String } * - * If the RHS is an intersection type A & B, we Lambda abstract on A instead and - * then recombine with & B. + * 2nd strategy: Used as a fallback if 1st strategy does not apply. It rewrites + * the RHS to a typed lambda abstraction. */ - def LambdaAbstract(boundSyms: List[Symbol])(error: (String, Position) => Unit)(implicit ctx: Context): Type = self match { - case AndType(l, r) => - AndType(l.LambdaAbstract(boundSyms)(error), r) - case _ => - val cls = self.typeSymbol - if (!cls.isClass) - error("right-hand side of parameterized alias type must refer to a class", cls.pos) - - val correspondingParamName: Map[Symbol, TypeName] = { - for { - (tparam, targ: TypeRef) <- cls.typeParams zip argInfos - if boundSyms contains targ.symbol - } yield targ.symbol -> tparam.name - }.toMap - - val correspondingNames = correspondingParamName.values.toSet - - def replacements(rt: RefinedType): List[Type] = - for (sym <- boundSyms) yield { - correspondingParamName get sym match { - case Some(name) => - TypeRef(RefinedThis(rt), name) - case None => - error(s"parameter $sym of type alias does not appear as type argument of the aliased $cls", sym.pos) - defn.AnyType - } + def parameterizeWith(boundSyms: List[Symbol])(implicit ctx: Context): Type = { + def matchParams(bsyms: List[Symbol], tparams: List[Symbol], targs: List[Type], + correspondingParamName: Map[Symbol, TypeName]): Type = { + if (bsyms.isEmpty) { + val correspondingNames = correspondingParamName.values.toSet + + def replacements(rt: RefinedType): List[Type] = + for (sym <- boundSyms) + yield TypeRef(RefinedThis(rt), correspondingParamName(sym)) + + def rewrite(tp: Type): Type = tp match { + case tp @ RefinedType(parent, name: TypeName) => + if (correspondingNames contains name) rewrite(parent) + else RefinedType( + rewrite(parent), + name, + rt => tp.refinedInfo.subst(boundSyms, replacements(rt))) + case tp => + tp } - def rewrite(tp: Type): Type = tp match { - case tp @ RefinedType(parent, name: TypeName) => - if (correspondingNames contains name) rewrite(parent) - else RefinedType( - rewrite(parent), - name, - rt => tp.refinedInfo.subst(boundSyms, replacements(rt))) - case tp => - tp + rewrite(self) + } + else if (tparams.isEmpty || targs.isEmpty) + LambdaAbstract(boundSyms) + else if (bsyms.head == targs.head.typeSymbol) + matchParams(bsyms.tail, tparams.tail, targs.tail, + correspondingParamName + (bsyms.head -> tparams.head.name.asTypeName)) + else + matchParams(bsyms, tparams.tail, targs.tail, correspondingParamName) + } + val cls = self.typeSymbol + if (cls.isClass) matchParams(boundSyms, cls.typeParams, argInfos, Map()) + else LambdaAbstract(boundSyms) + } + + /** The typed lambda abstraction of this type `T` relative to `boundSyms`. + * This is: + * + * LambdaXYZ{ type Apply = subst(T) } + * + * where XYZ reflets that variances of the bound symbols and + * `subst` is a substitution that replaces every bound symbol sym_i by + * `this.Arg$i`. + * + * TypeBounds are lambda abstracting by lambda abstracting their upper bound. + */ + def LambdaAbstract(boundSyms: List[Symbol])(implicit ctx: Context): Type = { + def expand(tp: Type) = { + val lambda = defn.lambdaTrait(boundSyms.map(_.variance)) + val substitutedRHS = (rt: RefinedType) => { + val argRefs = boundSyms.indices.toList.map(i => + RefinedThis(rt).select(tpnme.lambdaArgName(i))) + tp.bounds.subst(boundSyms, argRefs) } + val res = RefinedType(lambda.typeRef, tpnme.Apply, substitutedRHS) + //println(i"lambda abstract $self wrt $boundSyms%, % --> $res") + res + } + self match { + case self @ TypeBounds(lo, hi) => + self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi))) + case _ => + expand(self) + } + } - rewrite(self) + /** Convert a type constructor `TC` with type parameters `T1, ..., Tn` to + * + * LambdaXYZ { Apply = TC[$hkArg$0, ..., $hkArg$n] } + * + * where XYZ is a corresponds to the variances of the type parameters. + */ + def EtaExpand(implicit ctx: Context): Type = { + val tparams = typeParams + self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams) + } + + /** If this type has a base type `B[T1, ..., Tn]` where the type parameters + * of `B` match one-by-one the variances of `tparams`, convert it to + * + * LambdaXYZ { type Apply = B[$hkArg$0, ..., $hkArg$n] } + * { type $hkArg$0 = T1; ...; type $hkArg$n = Tn } + * + * A type parameter matches a varianve V if it has V as its variance or if V == 0. + */ + def EtaLiftTo(tparams: List[Symbol])(implicit ctx: Context): Type = { + def tryLift(bcs: List[ClassSymbol]): Type = bcs match { + case bc :: bcs1 => + val tp = self.baseTypeWithArgs(bc) + val targs = tp.argInfos + val tycon = tp.withoutArgs(targs) + def variancesMatch(param1: Symbol, param2: Symbol) = + param2.variance == param2.variance || param2.variance == 0 + if ((tycon.typeParams corresponds tparams)(variancesMatch)) { + val expanded = tycon.EtaExpand + val res = (expanded /: targs)((partialInst, arg) => + RefinedType(partialInst, partialInst.typeParams.head.name, arg.bounds)) + hk.println(i"eta lifting $self --> $res") + res + } + else tryLift(bcs1) + case nil => + NoType + } + if (tparams.isEmpty) NoType else tryLift(self.baseClasses) } }
\ No newline at end of file |