diff options
author | odersky <odersky@gmail.com> | 2017-04-06 14:12:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-06 14:12:22 +0200 |
commit | 09cc23726069fa04cbfeec55a9fa4bb8e4a02ff9 (patch) | |
tree | 604b5580371851334c05566986538f209ad55544 /compiler/src/dotty/tools/dotc/core | |
parent | 62c2a1e2d6265cf7f096e4c4e51e4e883bce1514 (diff) | |
parent | e800987c671c101a3643bfbaf93873db80ced9f3 (diff) | |
download | dotty-09cc23726069fa04cbfeec55a9fa4bb8e4a02ff9.tar.gz dotty-09cc23726069fa04cbfeec55a9fa4bb8e4a02ff9.tar.bz2 dotty-09cc23726069fa04cbfeec55a9fa4bb8e4a02ff9.zip |
Merge pull request #2121 from dotty-staging/change-merge-method-poly
Refactor lambda types
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core')
25 files changed, 805 insertions, 742 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Constants.scala b/compiler/src/dotty/tools/dotc/core/Constants.scala index 1892e4bdc..ed388b7ec 100644 --- a/compiler/src/dotty/tools/dotc/core/Constants.scala +++ b/compiler/src/dotty/tools/dotc/core/Constants.scala @@ -169,12 +169,12 @@ object Constants { def convertTo(pt: Type)(implicit ctx: Context): Constant = { def classBound(pt: Type): Type = pt.dealias.stripTypeVar match { case tref: TypeRef if !tref.symbol.isClass => classBound(tref.info.bounds.lo) - case param: PolyParam => + case param: TypeParamRef => ctx.typerState.constraint.entry(param) match { case TypeBounds(lo, hi) => if (hi.classSymbol.isPrimitiveValueClass) hi //constrain further with high bound else classBound(lo) - case NoType => classBound(param.binder.paramBounds(param.paramNum).lo) + case NoType => classBound(param.binder.paramInfos(param.paramNum).lo) case inst => classBound(inst) } case pt => pt diff --git a/compiler/src/dotty/tools/dotc/core/Constraint.scala b/compiler/src/dotty/tools/dotc/core/Constraint.scala index 50136a26c..a6c21c0d1 100644 --- a/compiler/src/dotty/tools/dotc/core/Constraint.scala +++ b/compiler/src/dotty/tools/dotc/core/Constraint.scala @@ -13,20 +13,20 @@ import config.Printers.constr /** Constraint over undetermined type parameters. Constraints are built * over values of the following types: * - * - PolyType A constraint constrains the type parameters of a set of PolyTypes - * - PolyParam The parameters of the constrained polytypes - * - TypeVar Every constrained parameter might be associated with a TypeVar - * that has the PolyParam as origin. + * - TypeLambda A constraint constrains the type parameters of a set of TypeLambdas + * - TypeParamRef The parameters of the constrained type lambdas + * - TypeVar Every constrained parameter might be associated with a TypeVar + * that has the TypeParamRef as origin. */ abstract class Constraint extends Showable { type This <: Constraint /** Does the constraint's domain contain the type parameters of `pt`? */ - def contains(pt: PolyType): Boolean + def contains(pt: TypeLambda): Boolean /** Does the constraint's domain contain the type parameter `param`? */ - def contains(param: PolyParam): Boolean + def contains(param: TypeParamRef): Boolean /** Does this constraint contain the type variable `tvar` and is it uninstantiated? */ def contains(tvar: TypeVar): Boolean @@ -34,43 +34,43 @@ abstract class Constraint extends Showable { /** The constraint entry for given type parameter `param`, or NoType if `param` is not part of * the constraint domain. Note: Low level, implementation dependent. */ - def entry(param: PolyParam): Type + def entry(param: TypeParamRef): Type /** The type variable corresponding to parameter `param`, or * NoType, if `param` is not in constrained or is not paired with a type variable. */ - def typeVarOfParam(param: PolyParam): Type + def typeVarOfParam(param: TypeParamRef): Type /** Is it known that `param1 <:< param2`? */ - def isLess(param1: PolyParam, param2: PolyParam): Boolean + def isLess(param1: TypeParamRef, param2: TypeParamRef): Boolean /** The parameters that are known to be smaller wrt <: than `param` */ - def lower(param: PolyParam): List[PolyParam] + def lower(param: TypeParamRef): List[TypeParamRef] /** The parameters that are known to be greater wrt <: than `param` */ - def upper(param: PolyParam): List[PolyParam] + def upper(param: TypeParamRef): List[TypeParamRef] /** lower(param) \ lower(butNot) */ - def exclusiveLower(param: PolyParam, butNot: PolyParam): List[PolyParam] + def exclusiveLower(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] /** upper(param) \ upper(butNot) */ - def exclusiveUpper(param: PolyParam, butNot: PolyParam): List[PolyParam] + def exclusiveUpper(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] /** The constraint bounds for given type parameter `param`. * Poly params that are known to be smaller or greater than `param` * are not contained in the return bounds. * @pre `param` is not part of the constraint domain. */ - def nonParamBounds(param: PolyParam): TypeBounds + def nonParamBounds(param: TypeParamRef): TypeBounds /** The lower bound of `param` including all known-to-be-smaller parameters */ - def fullLowerBound(param: PolyParam)(implicit ctx: Context): Type + def fullLowerBound(param: TypeParamRef)(implicit ctx: Context): Type /** The upper bound of `param` including all known-to-be-greater parameters */ - def fullUpperBound(param: PolyParam)(implicit ctx: Context): Type + def fullUpperBound(param: TypeParamRef)(implicit ctx: Context): Type /** The bounds of `param` including all known-to-be-smaller and -greater parameters */ - def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds + def fullBounds(param: TypeParamRef)(implicit ctx: Context): TypeBounds /** A new constraint which is derived from this constraint by adding * entries for all type parameters of `poly`. @@ -79,7 +79,7 @@ abstract class Constraint extends Showable { * satisfiability but will solved to give instances of * type variables. */ - def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This + def add(poly: TypeLambda, tvars: List[TypeVar])(implicit ctx: Context): This /** A new constraint which is derived from this constraint by updating * the entry for parameter `param` to `tp`. @@ -90,18 +90,18 @@ abstract class Constraint extends Showable { * * @pre `this contains param`. */ - def updateEntry(param: PolyParam, tp: Type)(implicit ctx: Context): This + def updateEntry(param: TypeParamRef, tp: Type)(implicit ctx: Context): This /** A constraint that includes the relationship `p1 <: p2`. * `<:` relationships between parameters ("edges") are propagated, but * non-parameter bounds are left alone. */ - def addLess(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This + def addLess(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This /** A constraint resulting from adding p2 = p1 to this constraint, and at the same * time transferring all bounds of p2 to p1 */ - def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This + def unify(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This /** A new constraint which is derived from this constraint by removing * the type parameter `param` from the domain and replacing all top-level occurrences @@ -109,25 +109,25 @@ abstract class Constraint extends Showable { * approximation of it if that is needed to avoid cycles. * Occurrences nested inside a refinement or prefix are not affected. */ - def replace(param: PolyParam, tp: Type)(implicit ctx: Context): This + def replace(param: TypeParamRef, tp: Type)(implicit ctx: Context): This /** Is entry associated with `pt` removable? This is the case if * all type parameters of the entry are associated with type variables * which have their `inst` fields set. */ - def isRemovable(pt: PolyType): Boolean + def isRemovable(pt: TypeLambda): Boolean /** A new constraint with all entries coming from `pt` removed. */ - def remove(pt: PolyType)(implicit ctx: Context): This + def remove(pt: TypeLambda)(implicit ctx: Context): This - /** The polytypes constrained by this constraint */ - def domainPolys: List[PolyType] + /** The type lambdas constrained by this constraint */ + def domainLambdas: List[TypeLambda] - /** The polytype parameters constrained by this constraint */ - def domainParams: List[PolyParam] + /** The type lambda parameters constrained by this constraint */ + def domainParams: List[TypeParamRef] /** Check whether predicate holds for all parameters in constraint */ - def forallParams(p: PolyParam => Boolean): Boolean + def forallParams(p: TypeParamRef => Boolean): Boolean /** Perform operation `op` on all typevars, or only on uninstantiated * typevars, depending on whether `uninstOnly` is set or not. @@ -143,6 +143,6 @@ abstract class Constraint extends Showable { /** Check that no constrained parameter contains itself as a bound */ def checkNonCyclic()(implicit ctx: Context): Unit - /** Check that constraint only refers to PolyParams bound by itself */ + /** Check that constraint only refers to TypeParamRefs bound by itself */ def checkClosed()(implicit ctx: Context): Unit } diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index a12936c58..de96f644a 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -42,12 +42,12 @@ trait ConstraintHandling { */ protected var homogenizeArgs = false - /** We are currently comparing polytypes. Used as a flag for + /** We are currently comparing type lambdas. Used as a flag for * optimization: when `false`, no need to do an expensive `pruneLambdaParams` */ - protected var comparedPolyTypes: Set[PolyType] = Set.empty + protected var comparedTypeLambdas: Set[TypeLambda] = Set.empty - private def addOneBound(param: PolyParam, bound: Type, isUpper: Boolean): Boolean = + private def addOneBound(param: TypeParamRef, bound: Type, isUpper: Boolean): Boolean = !constraint.contains(param) || { def occursIn(bound: Type): Boolean = { val b = bound.dealias @@ -75,7 +75,7 @@ trait ConstraintHandling { * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure * that `param >: bound`. */ - def narrowedBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = { + def narrowedBound(param: TypeParamRef, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = { val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param) val saved = homogenizeArgs homogenizeArgs = Config.alignArgsInAnd @@ -85,7 +85,7 @@ trait ConstraintHandling { finally homogenizeArgs = saved } - protected def addUpperBound(param: PolyParam, bound: Type): Boolean = { + protected def addUpperBound(param: TypeParamRef, bound: Type): Boolean = { def description = i"constraint $param <: $bound to\n$constraint" if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) { def msg = s"!!! instantiated to Nothing: $param, constraint = ${constraint.show}" @@ -101,7 +101,7 @@ trait ConstraintHandling { res } - protected def addLowerBound(param: PolyParam, bound: Type): Boolean = { + protected def addLowerBound(param: TypeParamRef, bound: Type): Boolean = { def description = i"constraint $param >: $bound to\n$constraint" constr.println(i"adding $description") val upper = constraint.upper(param) @@ -112,7 +112,7 @@ trait ConstraintHandling { res } - protected def addLess(p1: PolyParam, p2: PolyParam): Boolean = { + protected def addLess(p1: TypeParamRef, p2: TypeParamRef): Boolean = { def description = i"ordering $p1 <: $p2 to\n$constraint" val res = if (constraint.isLess(p2, p1)) unify(p2, p1) @@ -133,7 +133,7 @@ trait ConstraintHandling { /** Make p2 = p1, transfer all bounds of p2 to p1 * @pre less(p1)(p2) */ - private def unify(p1: PolyParam, p2: PolyParam): Boolean = { + private def unify(p1: TypeParamRef, p2: TypeParamRef): Boolean = { constr.println(s"unifying $p1 $p2") assert(constraint.isLess(p1, p2)) val down = constraint.exclusiveLower(p2, p1) @@ -191,7 +191,7 @@ trait ConstraintHandling { * @return the instantiating type * @pre `param` is in the constraint's domain. */ - final def approximation(param: PolyParam, fromBelow: Boolean): Type = { + final def approximation(param: TypeParamRef, fromBelow: Boolean): Type = { val avoidParam = new TypeMap { override def stopAtStatic = true def apply(tp: Type) = mapOver { @@ -235,7 +235,7 @@ trait ConstraintHandling { * a lower bound instantiation can be a singleton type only if the upper bound * is also a singleton type. */ - def instanceType(param: PolyParam, fromBelow: Boolean): Type = { + def instanceType(param: TypeParamRef, fromBelow: Boolean): Type = { def upperBound = constraint.fullUpperBound(param) def isSingleton(tp: Type): Boolean = tp match { case tp: SingletonType => true @@ -301,26 +301,26 @@ trait ConstraintHandling { } /** The current bounds of type parameter `param` */ - final def bounds(param: PolyParam): TypeBounds = { + final def bounds(param: TypeParamRef): TypeBounds = { val e = constraint.entry(param) - if (e.exists) e.bounds else param.binder.paramBounds(param.paramNum) + if (e.exists) e.bounds else param.binder.paramInfos(param.paramNum) } - /** Add polytype `pt`, possibly with type variables `tvars`, to current constraint + /** Add type lambda `tl`, possibly with type variables `tvars`, to current constraint * and propagate all bounds. * @param tvars See Constraint#add */ - def addToConstraint(pt: PolyType, tvars: List[TypeVar]): Unit = + def addToConstraint(tl: TypeLambda, tvars: List[TypeVar]): Unit = assert { - checkPropagated(i"initialized $pt") { - constraint = constraint.add(pt, tvars) - pt.paramNames.indices.forall { i => - val param = PolyParam(pt, i) + checkPropagated(i"initialized $tl") { + constraint = constraint.add(tl, tvars) + tl.paramNames.indices.forall { i => + val param = TypeParamRef(tl, i) val bounds = constraint.nonParamBounds(param) val lower = constraint.lower(param) val upper = constraint.upper(param) if (lower.nonEmpty && !bounds.lo.isRef(defn.NothingClass) || - upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr.println(i"INIT*** $pt") + upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr.println(i"INIT*** $tl") lower.forall(addOneBound(_, bounds.hi, isUpper = true)) && upper.forall(addOneBound(_, bounds.lo, isUpper = false)) } @@ -328,7 +328,7 @@ trait ConstraintHandling { } /** Can `param` be constrained with new bounds? */ - final def canConstrain(param: PolyParam): Boolean = + final def canConstrain(param: TypeParamRef): Boolean = !frozenConstraint && (constraint contains param) /** Add constraint `param <: bound` if `fromBelow` is false, `param >: bound` otherwise. @@ -338,7 +338,7 @@ trait ConstraintHandling { * not be AndTypes and lower bounds may not be OrTypes. This is assured by the * way isSubType is organized. */ - protected def addConstraint(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean = { + protected def addConstraint(param: TypeParamRef, bound: Type, fromBelow: Boolean): Boolean = { def description = i"constr $param ${if (fromBelow) ">:" else "<:"} $bound:\n$constraint" //checkPropagated(s"adding $description")(true) // DEBUG in case following fails checkPropagated(s"added $description") { @@ -357,12 +357,12 @@ trait ConstraintHandling { * missing. */ def pruneLambdaParams(tp: Type) = - if (comparedPolyTypes.nonEmpty) { + if (comparedTypeLambdas.nonEmpty) { val approx = new ApproximatingTypeMap { def apply(t: Type): Type = t match { - case t @ PolyParam(pt: PolyType, n) if comparedPolyTypes contains pt => + case t @ TypeParamRef(tl: TypeLambda, n) if comparedTypeLambdas contains tl => val effectiveVariance = if (fromBelow) -variance else variance - val bounds = pt.paramBounds(n) + val bounds = tl.paramInfos(n) if (effectiveVariance > 0) bounds.lo else if (effectiveVariance < 0) bounds.hi else NoType @@ -374,7 +374,7 @@ trait ConstraintHandling { } else tp - def addParamBound(bound: PolyParam) = + def addParamBound(bound: TypeParamRef) = if (fromBelow) addLess(bound, param) else addLess(param, bound) /** Drop all constrained parameters that occur at the toplevel in `bound` and @@ -419,7 +419,7 @@ trait ConstraintHandling { else NoType case bound: TypeVar if constraint contains bound.origin => prune(bound.underlying) - case bound: PolyParam => + case bound: TypeParamRef => constraint.entry(bound) match { case NoType => pruneLambdaParams(bound) case _: TypeBounds => @@ -434,7 +434,7 @@ trait ConstraintHandling { } try bound match { - case bound: PolyParam if constraint contains bound => + case bound: TypeParamRef if constraint contains bound => addParamBound(bound) case _ => val pbound = prune(bound) @@ -446,7 +446,7 @@ trait ConstraintHandling { } /** Instantiate `param` to `tp` if the constraint stays satisfiable */ - protected def tryInstantiate(param: PolyParam, tp: Type): Boolean = { + protected def tryInstantiate(param: TypeParamRef, tp: Type): Boolean = { val saved = constraint constraint = if (addConstraint(param, tp, fromBelow = true) && @@ -461,7 +461,7 @@ trait ConstraintHandling { val saved = frozenConstraint frozenConstraint = true for (p <- constraint.domainParams) { - def check(cond: => Boolean, q: PolyParam, ordering: String, explanation: String): Unit = + def check(cond: => Boolean, q: TypeParamRef, ordering: String, explanation: String): Unit = assert(cond, i"propagation failure for $p $ordering $q: $explanation\n$msg") for (u <- constraint.upper(p)) check(bounds(p).hi <:< bounds(u).hi, u, "<:", "upper bound not propagated") diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 39b46cbfe..b70fcb093 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -150,9 +150,9 @@ class Definitions { private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { - val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) - val tparamBounds = tparamNames map (_ => TypeBounds.empty) - val ptype = PolyType(tparamNames, tparamNames.map(alwaysZero))(_ => tparamBounds, resultTypeFn) + val tparamNames = PolyType.syntheticParamNames(typeParamCount) + val tparamInfos = tparamNames map (_ => TypeBounds.empty) + val ptype = PolyType(tparamNames)(_ => tparamInfos, resultTypeFn) enterMethod(cls, name, ptype, flags) } @@ -242,7 +242,7 @@ class Definitions { lazy val Any_## = enterMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) lazy val Any_getClass = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) lazy val Any_isInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) - lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) + lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, TypeParamRef(_, 0), Final) def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode, Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf) @@ -268,7 +268,7 @@ class Definitions { lazy val Object_eq = enterMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) lazy val Object_ne = enterMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) lazy val Object_synchronized = enterPolyMethod(ObjectClass, nme.synchronized_, 1, - pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final) + pt => MethodType(List(TypeParamRef(pt, 0)), TypeParamRef(pt, 0)), Final) lazy val Object_clone = enterMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) lazy val Object_finalize = enterMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) lazy val Object_notify = enterMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) @@ -283,7 +283,7 @@ class Definitions { /** Dummy method needed by elimByName */ lazy val dummyApply = enterPolyMethod( OpsPackageClass, nme.dummyApply, 1, - pt => MethodType(List(FunctionOf(Nil, PolyParam(pt, 0))), PolyParam(pt, 0))) + pt => MethodType(List(FunctionOf(Nil, TypeParamRef(pt, 0))), TypeParamRef(pt, 0))) /** Method representing a throw */ lazy val throwMethod = enterMethod(OpsPackageClass, nme.THROWkw, diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index f726cd0d1..7341b96af 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -213,7 +213,7 @@ object Denotations { def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = info.member(name.toTermName).requiredSymbol(x=> - (x is Method) && x.info.paramTypess == List(argTypes) + (x is Method) && x.info.paramInfoss == List(argTypes) ).asTerm def requiredMethodRef(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermRef = requiredMethod(name, argTypes).termRef @@ -252,13 +252,12 @@ object Denotations { else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging } - /** Merge two lists of names. If names in corresponding positions match, keep them, + /** Merge parameter names of lambda types. If names in corresponding positions match, keep them, * otherwise generate new synthetic names. */ - def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = { - for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped) - yield if (name1 == name2) name1 else syntheticName(idx) - }.toList + private def mergeParamNames(tp1: LambdaType, tp2: LambdaType): List[tp1.ThisName] = + (for ((name1, name2, idx) <- (tp1.paramNames, tp2.paramNames, tp1.paramNames.indices).zipped) + yield if (name1 == name2) name1 else tp1.companion.syntheticParamName(idx)).toList /** Form a denotation by conjoining with denotation `that`. * @@ -308,27 +307,17 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp1 case _ => mergeConflict(tp1, tp2) } - case tp1: MethodType if isTerm => + case tp1: MethodOrPoly => tp2 match { - case tp2: MethodType if ctx.typeComparer.matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), - tp1.paramTypes, + case tp2: MethodOrPoly + if ctx.typeComparer.matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit => + tp1.derivedLambdaType( + mergeParamNames(tp1, tp2), tp1.paramInfos, infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) case _ => mergeConflict(tp1, tp2) } - case tp1: PolyType if isTerm => - tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => - tp1.derivedPolyType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramBounds, - infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) - case _: MethodicType => - mergeConflict(tp1, tp2) - } case _ => tp1 & tp2 } @@ -471,23 +460,14 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp2 case _ => mergeConflict(tp1, tp2) } - case tp1: MethodType => - tp2 match { - case tp2: MethodType - if ctx.typeComparer.matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), - tp1.paramTypes, tp1.resultType | tp2.resultType.subst(tp2, tp1)) - case _ => - mergeConflict(tp1, tp2) - } - case tp1: PolyType => + case tp1: MethodOrPoly => tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => - tp1.derivedPolyType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramBounds, tp1.resultType | tp2.resultType.subst(tp2, tp1)) + case tp2: MethodOrPoly + if ctx.typeComparer.matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit => + tp1.derivedLambdaType( + mergeParamNames(tp1, tp2), tp1.paramInfos, + tp1.resultType | tp2.resultType.subst(tp2, tp1)) case _ => mergeConflict(tp1, tp2) } diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 406a84af6..c835f677e 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -34,7 +34,7 @@ object Mode { * context with typerstate and constraint. This is typically done when we * cache the eligibility of implicits. Caching needs to be done across different constraints. * Therefore, if TypevarsMissContext is set, subtyping becomes looser, and assumes - * that PolyParams can be sub- and supertypes of anything. See TypeComparer. + * that TypeParamRefs can be sub- and supertypes of anything. See TypeComparer. */ val TypevarsMissContext = newMode(4, "TypevarsMissContext") val CheckCyclic = newMode(5, "CheckCyclic") diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index ea905c19f..240ad359b 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -196,6 +196,31 @@ object NameOps { if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName else likeTyped(ctx.freshName(name ++ NameTransformer.NAME_JOIN_STRING))) + /** Name with variance prefix: `+` for covariant, `-` for contravariant */ + def withVariance(v: Int): N = + if (hasVariance) dropVariance.withVariance(v) + else v match { + case -1 => likeTyped('-' +: name) + case 1 => likeTyped('+' +: name) + case 0 => name + } + + /** Does name have a `+`/`-` variance prefix? */ + def hasVariance: Boolean = + name.nonEmpty && name.head == '+' || name.head == '-' + + /** Drop variance prefix if name has one */ + def dropVariance: N = if (hasVariance) likeTyped(name.tail) else name + + /** The variance as implied by the variance prefix, or 0 if there is + * no variance prefix. + */ + def variance = name.head match { + case '-' => -1 + case '+' => 1 + case _ => 0 + } + /** Translate a name into a list of simple TypeNames and TermNames. * In all segments before the last, type/term is determined by whether * the following separator char is '.' or '#'. The last segment @@ -271,7 +296,6 @@ object NameOps { else -1 } - /** The number of hops specified in an outer-select name */ def outerSelectHops: Int = { require(isOuterSelect) diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 61dd5a445..84b0bfc6d 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -14,13 +14,13 @@ import annotation.tailrec object OrderingConstraint { - type ArrayValuedMap[T] = SimpleMap[PolyType, Array[T]] + type ArrayValuedMap[T] = SimpleMap[TypeLambda, Array[T]] /** The type of `OrderingConstraint#boundsMap` */ type ParamBounds = ArrayValuedMap[Type] /** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */ - type ParamOrdering = ArrayValuedMap[List[PolyParam]] + type ParamOrdering = ArrayValuedMap[List[TypeParamRef]] /** A new constraint with given maps */ private def newConstraint(boundsMap: ParamBounds, lowerMap: ParamOrdering, upperMap: ParamOrdering)(implicit ctx: Context) : OrderingConstraint = { @@ -32,11 +32,11 @@ object OrderingConstraint { /** A lens for updating a single entry array in one of the three constraint maps */ abstract class ConstraintLens[T <: AnyRef: ClassTag] { - def entries(c: OrderingConstraint, poly: PolyType): Array[T] - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[T])(implicit ctx: Context): OrderingConstraint + def entries(c: OrderingConstraint, poly: TypeLambda): Array[T] + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[T])(implicit ctx: Context): OrderingConstraint def initial: T - def apply(c: OrderingConstraint, poly: PolyType, idx: Int) = { + def apply(c: OrderingConstraint, poly: TypeLambda, idx: Int) = { val es = entries(c, poly) if (es == null) initial else es(idx) } @@ -47,7 +47,7 @@ object OrderingConstraint { * parts of `current` which are not shared by `prev`. */ def update(prev: OrderingConstraint, current: OrderingConstraint, - poly: PolyType, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = { + poly: TypeLambda, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = { var es = entries(current, poly) if (es != null && (es(idx) eq entry)) current else { @@ -68,38 +68,38 @@ object OrderingConstraint { } def update(prev: OrderingConstraint, current: OrderingConstraint, - param: PolyParam, entry: T)(implicit ctx: Context): OrderingConstraint = + param: TypeParamRef, entry: T)(implicit ctx: Context): OrderingConstraint = update(prev, current, param.binder, param.paramNum, entry) def map(prev: OrderingConstraint, current: OrderingConstraint, - poly: PolyType, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint = + poly: TypeLambda, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint = update(prev, current, poly, idx, f(apply(current, poly, idx))) def map(prev: OrderingConstraint, current: OrderingConstraint, - param: PolyParam, f: T => T)(implicit ctx: Context): OrderingConstraint = + param: TypeParamRef, f: T => T)(implicit ctx: Context): OrderingConstraint = map(prev, current, param.binder, param.paramNum, f) } val boundsLens = new ConstraintLens[Type] { - def entries(c: OrderingConstraint, poly: PolyType): Array[Type] = + def entries(c: OrderingConstraint, poly: TypeLambda): Array[Type] = c.boundsMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[Type])(implicit ctx: Context): OrderingConstraint = + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[Type])(implicit ctx: Context): OrderingConstraint = newConstraint(c.boundsMap.updated(poly, entries), c.lowerMap, c.upperMap) def initial = NoType } - val lowerLens = new ConstraintLens[List[PolyParam]] { - def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] = + val lowerLens = new ConstraintLens[List[TypeParamRef]] { + def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] = c.lowerMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint = + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(implicit ctx: Context): OrderingConstraint = newConstraint(c.boundsMap, c.lowerMap.updated(poly, entries), c.upperMap) def initial = Nil } - val upperLens = new ConstraintLens[List[PolyParam]] { - def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] = + val upperLens = new ConstraintLens[List[TypeParamRef]] { + def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] = c.upperMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint = + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(implicit ctx: Context): OrderingConstraint = newConstraint(c.boundsMap, c.lowerMap, c.upperMap.updated(poly, entries)) def initial = Nil } @@ -109,20 +109,20 @@ import OrderingConstraint._ /** Constraint over undetermined type parameters that keeps separate maps to * reflect parameter orderings. - * @param boundsMap a map from PolyType to arrays. + * @param boundsMap a map from TypeLambda to arrays. * Each array contains twice the number of entries as there a type parameters - * in the PolyType. The first half of the array contains the type bounds that constrain the - * polytype's type parameters. The second half might contain type variables that + * in the TypeLambda. The first half of the array contains the type bounds that constrain the + * lambda's type parameters. The second half might contain type variables that * track the corresponding parameters, or is left empty (filled with nulls). * An instantiated type parameter is represented by having its instance type in * the corresponding array entry. The dual use of arrays for poly params * and typevars is to save space and hopefully gain some speed. * - * @param lowerMap a map from PolyTypes to arrays. Each array entry corresponds - * to a parameter P of the polytype; it contains all constrained parameters + * @param lowerMap a map from TypeLambdas to arrays. Each array entry corresponds + * to a parameter P of the type lambda; it contains all constrained parameters * Q that are known to be smaller than P, i.e. Q <: P. - * @param upperMap a map from PolyTypes to arrays. Each array entry corresponds - * to a parameter P of the polytype; it contains all constrained parameters + * @param upperMap a map from TypeLambdas to arrays. Each array entry corresponds + * to a parameter P of the type lambda; it contains all constrained parameters * Q that are known to be greater than P, i.e. P <: Q. */ class OrderingConstraint(private val boundsMap: ParamBounds, @@ -141,7 +141,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, entries(paramCount(entries) + n) /** The `boundsMap` entry corresponding to `param` */ - def entry(param: PolyParam): Type = { + def entry(param: TypeParamRef): Type = { val entries = boundsMap(param.binder) if (entries == null) NoType else entries(param.paramNum) @@ -149,9 +149,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds, // ----------- Contains tests -------------------------------------------------- - def contains(pt: PolyType): Boolean = boundsMap(pt) != null + def contains(pt: TypeLambda): Boolean = boundsMap(pt) != null - def contains(param: PolyParam): Boolean = { + def contains(param: TypeParamRef): Boolean = { val entries = boundsMap(param.binder) entries != null && isBounds(entries(param.paramNum)) } @@ -167,43 +167,43 @@ class OrderingConstraint(private val boundsMap: ParamBounds, // ---------- Dependency handling ---------------------------------------------- - def lower(param: PolyParam): List[PolyParam] = lowerLens(this, param.binder, param.paramNum) - def upper(param: PolyParam): List[PolyParam] = upperLens(this, param.binder, param.paramNum) + def lower(param: TypeParamRef): List[TypeParamRef] = lowerLens(this, param.binder, param.paramNum) + def upper(param: TypeParamRef): List[TypeParamRef] = upperLens(this, param.binder, param.paramNum) - def minLower(param: PolyParam): List[PolyParam] = { + def minLower(param: TypeParamRef): List[TypeParamRef] = { val all = lower(param) all.filterNot(p => all.exists(isLess(p, _))) } - def minUpper(param: PolyParam): List[PolyParam] = { + def minUpper(param: TypeParamRef): List[TypeParamRef] = { val all = upper(param) all.filterNot(p => all.exists(isLess(_, p))) } - def exclusiveLower(param: PolyParam, butNot: PolyParam): List[PolyParam] = + def exclusiveLower(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] = lower(param).filterNot(isLess(_, butNot)) - def exclusiveUpper(param: PolyParam, butNot: PolyParam): List[PolyParam] = + def exclusiveUpper(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] = upper(param).filterNot(isLess(butNot, _)) -// ---------- Info related to PolyParams ------------------------------------------- +// ---------- Info related to TypeParamRefs ------------------------------------------- - def isLess(param1: PolyParam, param2: PolyParam): Boolean = + def isLess(param1: TypeParamRef, param2: TypeParamRef): Boolean = upper(param1).contains(param2) - def nonParamBounds(param: PolyParam): TypeBounds = + def nonParamBounds(param: TypeParamRef): TypeBounds = entry(param).asInstanceOf[TypeBounds] - def fullLowerBound(param: PolyParam)(implicit ctx: Context): Type = + def fullLowerBound(param: TypeParamRef)(implicit ctx: Context): Type = (nonParamBounds(param).lo /: minLower(param))(_ | _) - def fullUpperBound(param: PolyParam)(implicit ctx: Context): Type = + def fullUpperBound(param: TypeParamRef)(implicit ctx: Context): Type = (nonParamBounds(param).hi /: minUpper(param))(_ & _) - def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds = + def fullBounds(param: TypeParamRef)(implicit ctx: Context): TypeBounds = nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param)) - def typeVarOfParam(param: PolyParam): Type = { + def typeVarOfParam(param: TypeParamRef): Type = { val entries = boundsMap(param.binder) if (entries == null) NoType else { @@ -212,15 +212,15 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } } -// ---------- Adding PolyTypes -------------------------------------------------- +// ---------- Adding TypeLambdas -------------------------------------------------- /** The list of parameters P such that, for a fresh type parameter Q: * * Q <: tp implies Q <: P and isUpper = true, or * tp <: Q implies P <: Q and isUpper = false */ - def dependentParams(tp: Type, isUpper: Boolean): List[PolyParam] = tp match { - case param: PolyParam if contains(param) => + def dependentParams(tp: Type, isUpper: Boolean): List[TypeParamRef] = tp match { + case param: TypeParamRef if contains(param) => param :: (if (isUpper) upper(param) else lower(param)) case tp: AndOrType => val ps1 = dependentParams(tp.tp1, isUpper) @@ -255,9 +255,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * * @param isUpper If true, `bound` is an upper bound, else a lower bound. */ - private def stripParams(tp: Type, paramBuf: mutable.ListBuffer[PolyParam], + private def stripParams(tp: Type, paramBuf: mutable.ListBuffer[TypeParamRef], isUpper: Boolean)(implicit ctx: Context): Type = tp match { - case param: PolyParam if contains(param) => + case param: TypeParamRef if contains(param) => if (!paramBuf.contains(param)) paramBuf += param NoType case tp: AndOrType if isUpper == tp.isAnd => @@ -275,16 +275,16 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * A top or bottom type if type consists only of dependent parameters. * @param isUpper If true, `bound` is an upper bound, else a lower bound. */ - private def normalizedType(tp: Type, paramBuf: mutable.ListBuffer[PolyParam], + private def normalizedType(tp: Type, paramBuf: mutable.ListBuffer[TypeParamRef], isUpper: Boolean)(implicit ctx: Context): Type = stripParams(tp, paramBuf, isUpper) .orElse(if (isUpper) defn.AnyType else defn.NothingType) - def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This = { + def add(poly: TypeLambda, tvars: List[TypeVar])(implicit ctx: Context): This = { assert(!contains(poly)) val nparams = poly.paramNames.length val entries1 = new Array[Type](nparams * 2) - poly.paramBounds.copyToArray(entries1, 0) + poly.paramInfos.copyToArray(entries1, 0) tvars.copyToArray(entries1, nparams) newConstraint(boundsMap.updated(poly, entries1), lowerMap, upperMap).init(poly) } @@ -293,12 +293,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * Update all bounds to be normalized and update ordering to account for * dependent parameters. */ - private def init(poly: PolyType)(implicit ctx: Context): This = { + private def init(poly: TypeLambda)(implicit ctx: Context): This = { var current = this - val loBuf, hiBuf = new mutable.ListBuffer[PolyParam] + val loBuf, hiBuf = new mutable.ListBuffer[TypeParamRef] var i = 0 while (i < poly.paramNames.length) { - val param = PolyParam(poly, i) + val param = TypeParamRef(poly, i) val bounds = nonParamBounds(param) val lo = normalizedType(bounds.lo, loBuf, isUpper = false) val hi = normalizedType(bounds.hi, hiBuf, isUpper = true) @@ -318,7 +318,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, /** Add the fact `param1 <: param2` to the constraint `current` and propagate * `<:<` relationships between parameters ("edges") but not bounds. */ - private def order(current: This, param1: PolyParam, param2: PolyParam)(implicit ctx: Context): This = + private def order(current: This, param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This = if (param1 == param2 || current.isLess(param1, param2)) this else { assert(contains(param1)) @@ -330,10 +330,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds, current2 } - def addLess(param1: PolyParam, param2: PolyParam)(implicit ctx: Context): This = + def addLess(param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This = order(this, param1, param2) - def updateEntry(current: This, param: PolyParam, tp: Type)(implicit ctx: Context): This = { + def updateEntry(current: This, param: TypeParamRef, tp: Type)(implicit ctx: Context): This = { var current1 = boundsLens.update(this, current, param, tp) tp match { case TypeBounds(lo, hi) => @@ -346,10 +346,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds, current1 } - def updateEntry(param: PolyParam, tp: Type)(implicit ctx: Context): This = + def updateEntry(param: TypeParamRef, tp: Type)(implicit ctx: Context): This = updateEntry(this, param, tp) - def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This = { + def unify(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This = { val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1) updateEntry(p1, p1Bounds).replace(p2, p1) } @@ -381,7 +381,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * would not find out where we need to approximate. Occurrences of parameters * that are not top-level are not affected. */ - def replace(param: PolyParam, tp: Type)(implicit ctx: Context): OrderingConstraint = { + def replace(param: TypeParamRef, tp: Type)(implicit ctx: Context): OrderingConstraint = { val replacement = tp.dealias.stripTypeVar if (param == replacement) this else { @@ -389,10 +389,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds, val poly = param.binder val idx = param.paramNum - def removeParam(ps: List[PolyParam]) = + def removeParam(ps: List[TypeParamRef]) = ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx) - def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int): Type = tp match { + def replaceParam(tp: Type, atPoly: TypeLambda, atIdx: Int): Type = tp match { case bounds @ TypeBounds(lo, hi) => def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = { @@ -404,7 +404,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } def normalize(tp: Type, isUpper: Boolean): Type = tp match { - case p: PolyParam if p.binder == atPoly && p.paramNum == atIdx => + case p: TypeParamRef if p.binder == atPoly && p.paramNum == atIdx => if (isUpper) defn.AnyType else defn.NothingType case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, normalize, isUpper) case _ => tp @@ -432,9 +432,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } } - def remove(pt: PolyType)(implicit ctx: Context): This = { + def remove(pt: TypeLambda)(implicit ctx: Context): This = { def removeFromOrdering(po: ParamOrdering) = { - def removeFromBoundss(key: PolyType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = { + def removeFromBoundss(key: TypeLambda, bndss: Array[List[TypeParamRef]]): Array[List[TypeParamRef]] = { val bndss1 = bndss.map(_.filterConserve(_.binder ne pt)) if (bndss.corresponds(bndss1)(_ eq _)) bndss else bndss1 } @@ -443,7 +443,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap)) } - def isRemovable(pt: PolyType): Boolean = { + def isRemovable(pt: TypeLambda): Boolean = { val entries = boundsMap(pt) @tailrec def allRemovable(last: Int): Boolean = if (last < 0) true @@ -456,24 +456,24 @@ class OrderingConstraint(private val boundsMap: ParamBounds, // ---------- Exploration -------------------------------------------------------- - def domainPolys: List[PolyType] = boundsMap.keys + def domainLambdas: List[TypeLambda] = boundsMap.keys - def domainParams: List[PolyParam] = + def domainParams: List[TypeParamRef] = for { (poly, entries) <- boundsMap.toList n <- 0 until paramCount(entries) if entries(n).exists - } yield PolyParam(poly, n) + } yield TypeParamRef(poly, n) - def forallParams(p: PolyParam => Boolean): Boolean = { + def forallParams(p: TypeParamRef => Boolean): Boolean = { boundsMap.foreachBinding { (poly, entries) => for (i <- 0 until paramCount(entries)) - if (isBounds(entries(i)) && !p(PolyParam(poly, i))) return false + if (isBounds(entries(i)) && !p(TypeParamRef(poly, i))) return false } true } - def foreachParam(p: (PolyType, Int) => Unit): Unit = + def foreachParam(p: (TypeLambda, Int) => Unit): Unit = boundsMap.foreachBinding { (poly, entries) => 0.until(poly.paramNames.length).foreach(p(poly, _)) } @@ -503,7 +503,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, merged } - def mergeParams(ps1: List[PolyParam], ps2: List[PolyParam]) = + def mergeParams(ps1: List[TypeParamRef], ps2: List[TypeParamRef]) = (ps1 /: ps2)((ps1, p2) => if (ps1.contains(p2)) ps1 else p2 :: ps1) def mergeEntries(e1: Type, e2: Type): Type = e1 match { @@ -532,13 +532,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } override def checkClosed()(implicit ctx: Context): Unit = { - def isFreePolyParam(tp: Type) = tp match { - case PolyParam(binder: PolyType, _) => !contains(binder) + def isFreeTypeParamRef(tp: Type) = tp match { + case TypeParamRef(binder: TypeLambda, _) => !contains(binder) case _ => false } def checkClosedType(tp: Type, where: String) = if (tp != null) - assert(!tp.existsPart(isFreePolyParam), i"unclosed constraint: $this refers to $tp in $where") + assert(!tp.existsPart(isFreeTypeParamRef), i"unclosed constraint: $this refers to $tp in $where") boundsMap.foreachBinding((_, tps) => tps.foreach(checkClosedType(_, "bounds"))) lowerMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "lower")))) upperMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "upper")))) @@ -567,7 +567,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def checkNonCyclic()(implicit ctx: Context): Unit = domainParams.foreach(checkNonCyclic) - private def checkNonCyclic(param: PolyParam)(implicit ctx: Context): Unit = + private def checkNonCyclic(param: TypeParamRef)(implicit ctx: Context): Unit = assert(!isLess(param, param), i"cyclic constraint involving $param in $this") // ---------- toText ----------------------------------------------------- @@ -584,7 +584,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, val uninstVarsText = " uninstVars = " ~ Text(uninstVars map (_.toText(printer)), ", ") ~ ";" val constrainedText = - " constrained types = " ~ Text(domainPolys map (_.toText(printer)), ", ") + " constrained types = " ~ Text(domainLambdas map (_.toText(printer)), ", ") val boundsText = " bounds = " ~ { val assocs = @@ -614,8 +614,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, case _ =>" := " + tp } val constrainedText = - " constrained types = " + domainPolys.mkString("\n") - val boundsText = + " constrained types = " + domainLambdas.mkString("\n") + val boundsText = domainLambdas " bounds = " + { val assocs = for (param <- domainParams) diff --git a/compiler/src/dotty/tools/dotc/core/TypeParamInfo.scala b/compiler/src/dotty/tools/dotc/core/ParamInfo.scala index 647c895db..46e378fc2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeParamInfo.scala +++ b/compiler/src/dotty/tools/dotc/core/ParamInfo.scala @@ -1,13 +1,15 @@ package dotty.tools.dotc.core -import Names.TypeName +import Names.Name import Contexts.Context -import Types.{Type, TypeBounds} +import Types.Type /** A common super trait of Symbol and LambdaParam. * Used to capture the attributes of type parameters which can be implemented as either. */ -trait TypeParamInfo { +trait ParamInfo { + + type ThisName <: Name /** Is this the info of a type parameter? Will return `false` for symbols * that are not type parameters. @@ -15,26 +17,30 @@ trait TypeParamInfo { def isTypeParam(implicit ctx: Context): Boolean /** The name of the type parameter */ - def paramName(implicit ctx: Context): TypeName + def paramName(implicit ctx: Context): ThisName /** The info of the type parameter */ - def paramBounds(implicit ctx: Context): TypeBounds + def paramInfo(implicit ctx: Context): Type /** The info of the type parameter as seen from a prefix type. * For type parameter symbols, this is the `memberInfo` as seen from `prefix`. - * For type lambda parameters, it's the same as `paramBounds` as + * For type lambda parameters, it's the same as `paramInfos` as * `asSeenFrom` has already been applied to the whole type lambda. */ - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds + def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context): Type /** The parameter bounds, or the completer if the type parameter * is an as-yet uncompleted symbol. */ - def paramBoundsOrCompleter(implicit ctx: Context): Type + def paramInfoOrCompleter(implicit ctx: Context): Type /** The variance of the type parameter */ def paramVariance(implicit ctx: Context): Int /** A type that refers to the parameter */ def paramRef(implicit ctx: Context): Type +} + +object ParamInfo { + type Of[N] = ParamInfo { type ThisName = N } }
\ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index bc3f96d91..e7928fd09 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -722,9 +722,6 @@ object StdNames { case _ => termName("_" + j) } - def syntheticParamNames(num: Int): List[TermName] = - (0 until num).map(syntheticParamName)(breakOut) - def localDummyName(clazz: Symbol)(implicit ctx: Context): TermName = LOCALDUMMY_PREFIX ++ clazz.name ++ ">" @@ -747,9 +744,6 @@ object StdNames { def syntheticTypeParamName(i: Int): TypeName = "X" + i - def syntheticTypeParamNames(num: Int): List[TypeName] = - (0 until num).map(syntheticTypeParamName)(breakOut) - final val Conforms = encode("<:<") final val Uninstantiated: TypeName = "?$" @@ -850,5 +844,4 @@ object StdNames { val tpnme = new ScalaTypeNames val jnme = new JavaTermNames val jtpnme = new JavaTypeNames - } diff --git a/compiler/src/dotty/tools/dotc/core/Substituters.scala b/compiler/src/dotty/tools/dotc/core/Substituters.scala index 23683608a..d565ec229 100644 --- a/compiler/src/dotty/tools/dotc/core/Substituters.scala +++ b/compiler/src/dotty/tools/dotc/core/Substituters.scala @@ -196,7 +196,7 @@ trait Substituters { this: Context => .mapOver(tp) } - final def substParam(tp: Type, from: ParamType, to: Type, theMap: SubstParamMap): Type = + final def substParam(tp: Type, from: ParamRef, to: Type, theMap: SubstParamMap): Type = tp match { case tp: BoundType => if (tp == from) to else tp @@ -216,7 +216,7 @@ trait Substituters { this: Context => final def substParams(tp: Type, from: BindingType, to: List[Type], theMap: SubstParamsMap): Type = tp match { - case tp: ParamType => + case tp: ParamRef => if (tp.binder == from) to(tp.paramNum) else tp case tp: NamedType => if (tp.currentSymbol.isStatic) tp @@ -269,7 +269,7 @@ trait Substituters { this: Context => def apply(tp: Type): Type = substRecThis(tp, from, to, this) } - final class SubstParamMap(from: ParamType, to: Type) extends DeepTypeMap { + final class SubstParamMap(from: ParamRef, to: Type) extends DeepTypeMap { def apply(tp: Type) = substParam(tp, from, to, this) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 27782698d..602848a50 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1146,14 +1146,13 @@ object SymDenotations { case tp: NamedType => hasSkolems(tp.prefix) case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo) case tp: RecType => hasSkolems(tp.parent) - case tp: PolyType => tp.paramBounds.exists(hasSkolems) || hasSkolems(tp.resType) - case tp: MethodType => tp.paramTypes.exists(hasSkolems) || hasSkolems(tp.resType) + case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi) + case tp: TypeVar => hasSkolems(tp.inst) case tp: ExprType => hasSkolems(tp.resType) case tp: HKApply => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems) + case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType) case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) - case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi) case tp: AnnotatedType => hasSkolems(tp.tpe) - case tp: TypeVar => hasSkolems(tp.inst) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 33aba4d13..95ff1cb75 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -384,7 +384,7 @@ object Symbols { * @param coord The coordinates of the symbol (a position or an index) * @param id A unique identifier of the symbol (unique per ContextBase) */ - class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with TypeParamInfo with printing.Showable { + class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with ParamInfo with printing.Showable { type ThisName <: Name @@ -513,12 +513,12 @@ object Symbols { */ def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition - // TypeParamInfo methods + // ParamInfo types and methods def isTypeParam(implicit ctx: Context) = denot.is(TypeParam) - def paramName(implicit ctx: Context) = name.asTypeName - def paramBounds(implicit ctx: Context) = denot.info.bounds - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this).bounds - def paramBoundsOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter + def paramName(implicit ctx: Context) = name.asInstanceOf[ThisName] + def paramInfo(implicit ctx: Context) = denot.info + def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this) + def paramInfoOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter def paramVariance(implicit ctx: Context) = denot.variance def paramRef(implicit ctx: Context) = denot.typeRef diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index ba3e6a461..23c3f96cc 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -21,6 +21,8 @@ import java.util.NoSuchElementException object TypeApplications { + type TypeParamInfo = ParamInfo.Of[TypeName] + /** Assert type is not a TypeBounds instance and return it unchanged */ val noBounds = (tp: Type) => tp match { case tp: TypeBounds => throw new AssertionError("no TypeBounds allowed") @@ -73,7 +75,7 @@ object TypeApplications { } def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = tp match { - case tp @ PolyType(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) + case tp @ HKTypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) case _ => None } } @@ -119,7 +121,7 @@ object TypeApplications { */ def EtaExpandIfHK(tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): List[Type] = if (tparams.isEmpty) args - else args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.paramBoundsOrCompleter)) + else args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.paramInfoOrCompleter)) /** A type map that tries to reduce (part of) the result type of the type lambda `tycon` * with the given `args`(some of which are wildcard arguments represented by type bounds). @@ -160,18 +162,18 @@ object TypeApplications { * result type. Using this mode, we can guarantee that `appliedTo` will never * produce a higher-kinded application with a type lambda as type constructor. */ - class Reducer(tycon: PolyType, args: List[Type])(implicit ctx: Context) extends TypeMap { + class Reducer(tycon: TypeLambda, args: List[Type])(implicit ctx: Context) extends TypeMap { private var available = (0 until args.length).toSet var allReplaced = true - def hasWildcardArg(p: PolyParam) = + def hasWildcardArg(p: TypeParamRef) = p.binder == tycon && args(p.paramNum).isInstanceOf[TypeBounds] - def canReduceWildcard(p: PolyParam) = + def canReduceWildcard(p: TypeParamRef) = !ctx.mode.is(Mode.AllowLambdaWildcardApply) || available.contains(p.paramNum) def apply(t: Type) = t match { - case t @ TypeAlias(p: PolyParam) if hasWildcardArg(p) && canReduceWildcard(p) => + case t @ TypeAlias(p: TypeParamRef) if hasWildcardArg(p) && canReduceWildcard(p) => available -= p.paramNum args(p.paramNum) - case p: PolyParam if p.binder == tycon => + case p: TypeParamRef if p.binder == tycon => args(p.paramNum) match { case TypeBounds(lo, hi) => if (ctx.mode.is(Mode.AllowLambdaWildcardApply)) { allReplaced = false; p } @@ -213,7 +215,7 @@ class TypeApplications(val self: Type) extends AnyVal { self match { case self: ClassInfo => self.cls.typeParams - case self: PolyType => + case self: HKTypeLambda => self.typeParams case self: TypeRef => val tsym = self.symbol @@ -251,7 +253,7 @@ class TypeApplications(val self: Type) extends AnyVal { def isHK(implicit ctx: Context): Boolean = self.dealias match { case self: TypeRef => self.info.isHK case self: RefinedType => false - case self: PolyType => true + case self: HKTypeLambda => true case self: SingletonType => false case self: TypeVar => // Using `origin` instead of `underlying`, as is done for typeParams, @@ -270,31 +272,6 @@ class TypeApplications(val self: Type) extends AnyVal { self } - /** Lambda abstract `self` with given type parameters. Examples: - * - * type T[X] = U becomes type T = [X] -> U - * type T[X] >: L <: U becomes type T >: L <: ([X] -> U) - * - * TODO: Handle parameterized lower bounds - */ - def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = { - def expand(tp: Type) = - PolyType( - tparams.map(_.paramName), tparams.map(_.paramVariance))( - tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds), - tl => tl.lifted(tparams, tp)) - if (tparams.isEmpty) self - else self match { - case self: TypeAlias => - self.derivedTypeAlias(expand(self.alias)) - case self @ TypeBounds(lo, hi) => - self.derivedTypeBounds( - if (lo.isRef(defn.NothingClass)) lo else expand(lo), - expand(hi)) - case _ => expand(self) - } - } - /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` * in a context where type parameters `U1,...,Un` are expected to * @@ -307,7 +284,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols - self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) + HKTypeLambda.fromParams(tparamsToUse, self.appliedTo(tparams map (_.typeRef))) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } @@ -362,11 +339,13 @@ class TypeApplications(val self: Type) extends AnyVal { if (hkParams.isEmpty) self else { def adaptArg(arg: Type): Type = arg match { - case arg @ PolyType(tparams, body) if + case arg @ HKTypeLambda(tparams, body) if !tparams.corresponds(hkParams)(_.paramVariance == _.paramVariance) && tparams.corresponds(hkParams)(varianceConforms) => - PolyType(tparams.map(_.paramName), hkParams.map(_.paramVariance))( - tl => arg.paramBounds.map(_.subst(arg, tl).bounds), + HKTypeLambda( + (tparams, hkParams).zipped.map((tparam, hkparam) => + tparam.paramName.withVariance(hkparam.paramVariance)))( + tl => arg.paramInfos.map(_.subst(arg, tl).bounds), tl => arg.resultType.subst(arg, tl) ) case arg @ TypeAlias(alias) => @@ -390,7 +369,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { val typParams = self.typeParams - def matchParams(t: Type, tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): Type = args match { + def matchParams(t: Type, tparams: List[ParamInfo], args: List[Type])(implicit ctx: Context): Type = args match { case arg :: args1 => try { val tparam :: tparams1 = tparams @@ -407,7 +386,7 @@ class TypeApplications(val self: Type) extends AnyVal { val dealiased = stripped.safeDealias if (args.isEmpty || ctx.erasedTypes) self else dealiased match { - case dealiased: PolyType => + case dealiased: HKTypeLambda => def tryReduce = if (!args.exists(_.isInstanceOf[TypeBounds])) { val followAlias = Config.simplifyApplications && { @@ -426,7 +405,7 @@ class TypeApplications(val self: Type) extends AnyVal { // In this case we should always dealias since we cannot handle // higher-kinded applications to wildcard arguments. dealiased - .derivedPolyType(resType = tycon.safeDealias.appliedTo(args1)) + .derivedLambdaType(resType = tycon.safeDealias.appliedTo(args1)) .appliedTo(args) case _ => val reducer = new Reducer(dealiased, args) @@ -435,6 +414,8 @@ class TypeApplications(val self: Type) extends AnyVal { else HKApply(dealiased, args) } tryReduce + case dealiased: PolyType => + dealiased.instantiate(args) case dealiased: AndOrType => dealiased.derivedAndOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args)) case dealiased: TypeAlias => @@ -475,7 +456,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** Turn this type, which is used as an argument for * type parameter `tparam`, into a TypeBounds RHS */ - final def toBounds(tparam: TypeParamInfo)(implicit ctx: Context): TypeBounds = self match { + final def toBounds(tparam: ParamInfo)(implicit ctx: Context): TypeBounds = self match { case self: TypeBounds => // this can happen for wildcard args self case _ => diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 57dde3288..da6d63387 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -280,7 +280,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => } thirdTry(tp1, tp2) - case tp1: PolyParam => + case tp1: TypeParamRef => def flagNothingBound = { if (!frozenConstraint && tp2.isRef(defn.NothingClass) && state.isGlobalCommittable) { def msg = s"!!! instantiated to Nothing: $tp1, constraint = ${constraint.show}" @@ -289,13 +289,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } true } - def comparePolyParam = + def compareTypeParamRef = ctx.mode.is(Mode.TypevarsMissContext) || isSubTypeWhenFrozen(bounds(tp1).hi, tp2) || { if (canConstrain(tp1)) addConstraint(tp1, tp2, fromBelow = false) && flagNothingBound else thirdTry(tp1, tp2) } - comparePolyParam + compareTypeParamRef case tp1: ThisType => val cls1 = tp1.cls tp2 match { @@ -373,8 +373,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private def thirdTry(tp1: Type, tp2: Type): Boolean = tp2 match { case tp2: NamedType => thirdTryNamed(tp1, tp2) - case tp2: PolyParam => - def comparePolyParam = + case tp2: TypeParamRef => + def compareTypeParamRef = (ctx.mode is Mode.TypevarsMissContext) || { val alwaysTrue = // The following condition is carefully formulated to catch all cases @@ -391,7 +391,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { else fourthTry(tp1, tp2) } } - comparePolyParam + compareTypeParamRef case tp2: RefinedType => def compareRefinedSlow: Boolean = { val name2 = tp2.refinedName @@ -428,9 +428,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { compareRec case tp2 @ HKApply(tycon2, args2) => compareHkApply2(tp1, tp2, tycon2, args2) - case tp2 @ PolyType(tparams2, body2) => - def compareHkLambda: Boolean = tp1.stripTypeVar match { - case tp1 @ PolyType(tparams1, body1) => + case tp2: HKTypeLambda => + def compareTypeLambda: Boolean = tp1.stripTypeVar match { + case tp1: HKTypeLambda => /* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail * The issue is that, logically, bounds should compare contravariantly, * but that would invalidate a pattern exploited in t2994: @@ -446,16 +446,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { */ def boundsOK = ctx.scala2Mode || - tparams1.corresponds(tparams2)((tparam1, tparam2) => - isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds)) - val saved = comparedPolyTypes - comparedPolyTypes += tp1 - comparedPolyTypes += tp2 + tp1.typeParams.corresponds(tp2.typeParams)((tparam1, tparam2) => + isSubType(tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo)) + val saved = comparedTypeLambdas + comparedTypeLambdas += tp1 + comparedTypeLambdas += tp2 try - variancesConform(tparams1, tparams2) && + variancesConform(tp1.typeParams, tp2.typeParams) && boundsOK && - isSubType(body1, body2.subst(tp2, tp1)) - finally comparedPolyTypes = saved + isSubType(tp1.resType, tp2.resType.subst(tp2, tp1)) + finally comparedTypeLambdas = saved case _ => if (!tp1.isHK) { tp2 match { @@ -466,7 +466,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } fourthTry(tp1, tp2) } - compareHkLambda + compareTypeLambda case OrType(tp21, tp22) => // Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22) // and analogously for T1 <: T21 | (T221 & T222) @@ -484,12 +484,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => } either(isSubType(tp1, tp21), isSubType(tp1, tp22)) || fourthTry(tp1, tp2) - case tp2: MethodType => + case tp2: MethodOrPoly => def compareMethod = tp1 match { - case tp1: MethodType => + case tp1: MethodOrPoly => (tp1.signature consistentParams tp2.signature) && - matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - (tp1.isImplicit == tp2.isImplicit) && + matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit && isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => false @@ -618,7 +618,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def isMatchingApply(tp1: Type): Boolean = tp1 match { case HKApply(tycon1, args1) => tycon1.dealias match { - case tycon1: PolyParam => + case tycon1: TypeParamRef => (tycon1 == tycon2 || canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) && isSubArgs(args1, args2, tparams) @@ -646,7 +646,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * and the resulting type application is a supertype of `tp1`, * or fallback to fourthTry. */ - def canInstantiate(tycon2: PolyParam): Boolean = { + def canInstantiate(tycon2: TypeParamRef): Boolean = { /** Let * @@ -669,10 +669,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val tparams1 = tparams1a.drop(lengthDiff) variancesConform(tparams1, tparams) && { if (lengthDiff > 0) - tycon1b = PolyType(tparams1.map(_.paramName), tparams1.map(_.paramVariance))( - tl => tparams1.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds), + tycon1b = HKTypeLambda(tparams1.map(_.paramName))( + tl => tparams1.map(tparam => tl.integrate(tparams, tparam.paramInfo).bounds), tl => tycon1a.appliedTo(args1.take(lengthDiff) ++ - tparams1.indices.toList.map(PolyParam(tl, _)))) + tparams1.indices.toList.map(TypeParamRef(tl, _)))) (ctx.mode.is(Mode.TypevarsMissContext) || tryInstantiate(tycon2, tycon1b.ensureHK)) && isSubType(tp1, tycon1b.appliedTo(args2)) @@ -725,7 +725,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { fallback(tycon2bounds.lo) tycon2 match { - case param2: PolyParam => + case param2: TypeParamRef => isMatchingApply(tp1) || { if (canConstrain(param2)) canInstantiate(param2) else compareLower(bounds(param2), tyconIsTypeRef = false) @@ -746,7 +746,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { */ def compareHkApply1(tp1: HKApply, tycon1: Type, args1: List[Type], tp2: Type): Boolean = tycon1 match { - case param1: PolyParam => + case param1: TypeParamRef => def canInstantiate = tp2 match { case AppliedType(tycon2, args2) => tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tycon2.typeParams) @@ -764,7 +764,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Subtype test for corresponding arguments in `args1`, `args2` according to * variances in type parameters `tparams`. */ - def isSubArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo]): Boolean = + def isSubArgs(args1: List[Type], args2: List[Type], tparams: List[ParamInfo]): Boolean = if (args1.isEmpty) args2.isEmpty else args2.nonEmpty && { val v = tparams.head.paramVariance @@ -806,7 +806,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def fix(tp: Type): Type = tp.stripTypeVar match { case tp: RecType => fix(tp.parent).substRecThis(tp, anchor) case tp @ RefinedType(parent, rname, rinfo) => tp.derivedRefinedType(fix(parent), rname, rinfo) - case tp: PolyParam => fixOrElse(bounds(tp).hi, tp) + case tp: TypeParamRef => fixOrElse(bounds(tp).hi, tp) case tp: TypeProxy => fixOrElse(tp.underlying, tp) case tp: AndOrType => tp.derivedAndOrType(fix(tp.tp1), fix(tp.tp2)) case tp => tp @@ -967,7 +967,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Defer constraining type variables when compared against prototypes */ def isMatchedByProto(proto: ProtoType, tp: Type) = tp.stripTypeVar match { - case tp: PolyParam if constraint contains tp => true + case tp: TypeParamRef if constraint contains tp => true case _ => proto.isMatchedBy(tp) } @@ -978,7 +978,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * type variable with (the corresponding type in) `tp2` instead. */ private def isCappable(tp: Type): Boolean = tp match { - case tp: PolyParam => constraint contains tp + case tp: TypeParamRef => constraint contains tp case tp: TypeProxy => isCappable(tp.underlying) case tp: AndOrType => isCappable(tp.tp1) || isCappable(tp.tp2) case _ => false @@ -1021,7 +1021,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2.widen match { case tp2: MethodType => // implicitness is ignored when matching - matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && + matchingParams(tp1, tp2) && matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) case tp2 => relaxed && tp1.paramNames.isEmpty && @@ -1031,7 +1031,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2.widen match { case tp2: PolyType => sameLength(tp1.paramNames, tp2.paramNames) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) + matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) case _ => false } @@ -1047,28 +1047,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } - /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ - def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match { - case formal1 :: rest1 => - formals2 match { - case formal2 :: rest2 => - (isSameTypeWhenFrozen(formal1, formal2) - || isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) - || isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && - matchingParams(rest1, rest2, isJava1, isJava2) - case nil => - false - } - case nil => - formals2.isEmpty - } - - /** Do generic types `poly1` and `poly2` have type parameters that - * have the same bounds (after renaming one set to the other)? + /** Do lambda types `lam1` and `lam2` have parameters that have the same types + * and the same implicit status? (after renaming one set to the other) */ - def matchingTypeParams(poly1: PolyType, poly2: PolyType): Boolean = - (poly1.paramBounds corresponds poly2.paramBounds)((b1, b2) => - isSameType(b1, b2.subst(poly2, poly1))) + def matchingParams(lam1: MethodOrPoly, lam2: MethodOrPoly): Boolean = { + /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ + def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match { + case formal1 :: rest1 => + formals2 match { + case formal2 :: rest2 => + val formal2a = if (lam2.isParamDependent) formal2.subst(lam2, lam1) else formal2 + (isSameTypeWhenFrozen(formal1, formal2a) + || lam1.isJava && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) + || lam2.isJava && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && + loop(rest1, rest2) + case nil => + false + } + case nil => + formals2.isEmpty + } + loop(lam1.paramInfos, lam2.paramInfos) + } // Type equality =:= @@ -1216,7 +1216,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Form a normalized conjunction of two types. * Note: For certain types, `&` is distributed inside the type. This holds for * all types which are not value types (e.g. TypeBounds, ClassInfo, - * ExprType, MethodType, PolyType). Also, when forming an `&`, + * ExprType, LambdaType). Also, when forming an `&`, * instantiated TypeVars are dereferenced and annotations are stripped. * Finally, refined types with the same refined name are * opportunistically merged. @@ -1245,7 +1245,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Form a normalized conjunction of two types. * Note: For certain types, `|` is distributed inside the type. This holds for * all types which are not value types (e.g. TypeBounds, ClassInfo, - * ExprType, MethodType, PolyType). Also, when forming an `|`, + * ExprType, LambdaType). Also, when forming an `|`, * instantiated TypeVars are dereferenced and annotations are stripped. * * Sometimes, the disjunction of two types cannot be formed because @@ -1276,20 +1276,20 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val tparams2 = tp2.typeParams if (tparams1.isEmpty) if (tparams2.isEmpty) op(tp1, tp2) - else original(tp1, tp2.appliedTo(tp2.typeParams.map(_.paramBoundsAsSeenFrom(tp2)))) + else original(tp1, tp2.appliedTo(tp2.typeParams.map(_.paramInfoAsSeenFrom(tp2)))) else if (tparams2.isEmpty) - original(tp1.appliedTo(tp1.typeParams.map(_.paramBoundsAsSeenFrom(tp1))), tp2) + original(tp1.appliedTo(tp1.typeParams.map(_.paramInfoAsSeenFrom(tp1))), tp2) else - PolyType( - paramNames = tpnme.syntheticTypeParamNames(tparams1.length), - variances = (tparams1, tparams2).zipped.map((tparam1, tparam2) => - (tparam1.paramVariance + tparam2.paramVariance) / 2))( - paramBoundsExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) => - tl.lifted(tparams1, tparam1.paramBoundsAsSeenFrom(tp1)).bounds & - tl.lifted(tparams2, tparam2.paramBoundsAsSeenFrom(tp2)).bounds), + HKTypeLambda( + paramNames = (HKTypeLambda.syntheticParamNames(tparams1.length), tparams1, tparams2) + .zipped.map((pname, tparam1, tparam2) => + pname.withVariance((tparam1.paramVariance + tparam2.paramVariance) / 2)))( + paramInfosExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) => + tl.integrate(tparams1, tparam1.paramInfoAsSeenFrom(tp1)).bounds & + tl.integrate(tparams2, tparam2.paramInfoAsSeenFrom(tp2)).bounds), resultTypeExp = tl => - original(tl.lifted(tparams1, tp1).appliedTo(tl.paramRefs), - tl.lifted(tparams2, tp2).appliedTo(tl.paramRefs))) + original(tl.integrate(tparams1, tp1).appliedTo(tl.paramRefs), + tl.integrate(tparams2, tp2).appliedTo(tl.paramRefs))) } /** Try to distribute `&` inside type, detect and handle conflicts @@ -1392,8 +1392,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp2: MethodType => def asGoodParams(formals1: List[Type], formals2: List[Type]) = (formals2 corresponds formals1)(isSubTypeWhenFrozen) - asGoodParams(tp1.paramTypes, tp2.paramTypes) && - (!asGoodParams(tp2.paramTypes, tp1.paramTypes) || + asGoodParams(tp1.paramInfos, tp2.paramInfos) && + (!asGoodParams(tp2.paramInfos, tp1.paramInfos) || isAsGood(tp1.resultType, tp2.resultType)) case _ => false @@ -1424,7 +1424,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = { println(ex"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint") def explainPoly(tp: Type) = tp match { - case tp: PolyParam => ctx.echo(s"polyparam ${tp.show} found in ${tp.binder.show}") + case tp: TypeParamRef => ctx.echo(s"TypeParamRef ${tp.show} found in ${tp.binder.show}") case tp: TypeRef if tp.symbol.exists => ctx.echo(s"typeref ${tp.show} found in ${tp.symbol.owner.show}") case tp: TypeVar => ctx.echo(s"typevar ${tp.show}, origin = ${tp.origin}") case _ => ctx.echo(s"${tp.show} is a ${tp.getClass}") @@ -1503,7 +1503,7 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { super.glb(tp1, tp2) } - override def addConstraint(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean = + override def addConstraint(param: TypeParamRef, bound: Type, fromBelow: Boolean): Boolean = traceIndented(i"add constraint $param ${if (fromBelow) ">:" else "<:"} $bound $frozenConstraint, constraint = ${ctx.typerState.constraint}") { super.addConstraint(param, bound, fromBelow) } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index fe3396fcb..f35752644 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -28,7 +28,7 @@ import scala.annotation.tailrec * WildcardType * ErrorType * - * only for isInstanceOf, asInstanceOf: PolyType, PolyParam, TypeBounds + * only for isInstanceOf, asInstanceOf: PolyType, TypeParamRef, TypeBounds * */ object TypeErasure { @@ -55,7 +55,7 @@ object TypeErasure { case ThisType(tref) => isErasedType(tref) case tp: MethodType => - tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType) + tp.paramInfos.forall(isErasedType) && isErasedType(tp.resultType) case tp @ ClassInfo(pre, _, parents, decls, _) => isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType) case NoType | NoPrefix | WildcardType | _: ErrorType | SuperType(_, _) => @@ -176,7 +176,7 @@ object TypeErasure { val erase = erasureFn(isJava, semiEraseVCs, sym.isConstructor, wildcardOK = false) def eraseParamBounds(tp: PolyType): Type = - tp.derivedPolyType( + tp.derivedLambdaType( tp.paramNames, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType) if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) @@ -186,7 +186,7 @@ object TypeErasure { case einfo: MethodType => if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass)) MethodType(Nil, defn.BoxedUnitType) - else if (sym.isAnonymousFunction && einfo.paramTypes.length > MaxImplementedFunctionArity) + else if (sym.isAnonymousFunction && einfo.paramInfos.length > MaxImplementedFunctionArity) MethodType(nme.ALLARGS :: Nil, JavaArrayType(defn.ObjectType) :: Nil, einfo.resultType) else einfo @@ -204,7 +204,7 @@ object TypeErasure { !tp.symbol.isClass && !tp.derivesFrom(defn.ObjectClass) && !tp.symbol.is(JavaDefined) - case tp: PolyParam => + case tp: TypeParamRef => !tp.derivesFrom(defn.ObjectClass) && !tp.binder.resultType.isInstanceOf[JavaMethodType] case tp: TypeAlias => isUnboundedGeneric(tp.alias) @@ -304,7 +304,7 @@ object TypeErasure { case _: ClassInfo => true case _ => false } - case tp: PolyParam => false + case tp: TypeParamRef => false case tp: TypeProxy => hasStableErasure(tp.superType) case tp: AndOrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) case _ => false @@ -382,13 +382,15 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean case tp: MethodType => def paramErasure(tpToErase: Type) = erasureFn(tp.isJava, semiEraseVCs, isConstructor, wildcardOK)(tpToErase) - val formals = tp.paramTypes.mapConserve(paramErasure) + val formals = tp.paramInfos.mapConserve(paramErasure) eraseResult(tp.resultType) match { case rt: MethodType => - tp.derivedMethodType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramTypes, rt.resultType) + tp.derivedLambdaType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramInfos, rt.resultType) case rt => - tp.derivedMethodType(tp.paramNames, formals, rt) + tp.derivedLambdaType(tp.paramNames, formals, rt) } + case tp: PolyType => + this(tp.resultType) case tp @ ClassInfo(pre, cls, classParents, decls, _) => if (cls is Package) tp else { @@ -517,6 +519,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if (inst.exists) sigName(inst) else tpnme.Uninstantiated case tp: TypeProxy => sigName(tp.underlying) + case tp: PolyType => + sigName(tp.resultType) case _: ErrorType | WildcardType => tpnme.WILDCARD case tp: WildcardType => diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 3d2906320..9593bfe93 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -157,7 +157,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. tp2 case tp1 => tp1 } - case tp: PolyParam => + case tp: TypeParamRef => typerState.constraint.typeVarOfParam(tp) orElse tp case _: ThisType | _: BoundType | NoPrefix => tp diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 206438d86..b33b3aa29 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -42,7 +42,7 @@ class TyperState(r: Reporter) extends DotClass with Showable { */ def instType(tvar: TypeVar)(implicit ctx: Context): Type = constraint.entry(tvar.origin) match { case _: TypeBounds => NoType - case tp: PolyParam => + case tp: TypeParamRef => var tvar1 = constraint.typeVarOfParam(tp) if (tvar1.exists) tvar1 else tp case tp => tp @@ -155,14 +155,14 @@ extends TyperState(r) { } override def gc()(implicit ctx: Context): Unit = { - val toCollect = new mutable.ListBuffer[PolyType] + val toCollect = new mutable.ListBuffer[TypeLambda] constraint foreachTypeVar { tvar => if (!tvar.inst.exists) { val inst = instType(tvar) if (inst.exists && (tvar.owningState eq this)) { tvar.inst = inst - val poly = tvar.origin.binder - if (constraint.isRemovable(poly)) toCollect += poly + val lam = tvar.origin.binder + if (constraint.isRemovable(lam)) toCollect += lam } } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c80107f93..83fb70aa1 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -54,10 +54,10 @@ object Types { * | | +--- ThisType * | | +--- SuperType * | | +--- ConstantType - * | | +--- MethodParam + * | | +--- TermParamRef * | | +----RecThis * | | +--- SkolemType - * | +- PolyParam + * | +- TypeParamRef * | +- RefinedOrRecType -+-- RefinedType * | | -+-- RecType * | +- HKApply @@ -65,12 +65,13 @@ object Types { * | +- ExprType * | +- AnnotatedType * | +- TypeVar - * | +- PolyType + * | +- HKTypeLambda * | * +- GroundType -+- AndType * +- OrType - * +- MethodType -----+- ImplicitMethodType - * | +- JavaMethodType + * +- MethodOrPoly ---+-- PolyType + * +-- MethodType ---+- ImplicitMethodType + * | +- JavaMethodType * +- ClassInfo * | * +- NoType @@ -103,7 +104,7 @@ object Types { final def isValueType: Boolean = this.isInstanceOf[ValueType] /** Is the is value type or type lambda? */ - final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[PolyType] + final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[TypeLambda] /** Does this type denote a stable reference (i.e. singleton type)? */ @tailrec final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { @@ -215,17 +216,15 @@ object Types { /** Is this the type of a method that has a repeated parameter type as * last parameter type? */ - def isVarArgsMethod(implicit ctx: Context): Boolean = this match { - case tp: PolyType => tp.resultType.isVarArgsMethod - case mt: MethodType => mt.paramTypes.nonEmpty && mt.paramTypes.last.isRepeatedParam + def isVarArgsMethod(implicit ctx: Context): Boolean = stripPoly match { + case mt: MethodType => mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam case _ => false } /** Is this the type of a method with a leading empty parameter list? */ - def isNullaryMethod(implicit ctx: Context): Boolean = this match { + def isNullaryMethod(implicit ctx: Context): Boolean = stripPoly match { case MethodType(Nil) => true - case tp: PolyType => tp.resultType.isNullaryMethod case _ => false } @@ -443,10 +442,10 @@ object Types { case tp: TermRef => go (tp.underlying match { case mt: MethodType - if mt.paramTypes.isEmpty && (tp.symbol is Stable) => mt.resultType + if mt.paramInfos.isEmpty && (tp.symbol is Stable) => mt.resultType case tp1 => tp1 }) - case tp: PolyParam => + case tp: TypeParamRef => goParam(tp) case tp: RecType => goRec(tp) @@ -541,9 +540,9 @@ object Types { } def goApply(tp: HKApply) = tp.tycon match { - case tl: PolyType => + case tl: HKTypeLambda => go(tl.resType).mapInfo(info => - tl.derivedLambdaAbstraction(tl.paramNames, tl.paramBounds, info).appliedTo(tp.args)) + tl.derivedLambdaAbstraction(tl.paramNames, tl.paramInfos, info).appliedTo(tp.args)) case _ => go(tp.superType) } @@ -563,7 +562,7 @@ object Types { // loadClassWithPrivateInnerAndSubSelf in ShowClassTests go(tp.cls.typeRef) orElse d } - def goParam(tp: PolyParam) = { + def goParam(tp: TypeParamRef) = { val next = tp.underlying ctx.typerState.constraint.entry(tp) match { case bounds: TypeBounds if bounds ne next => @@ -814,6 +813,12 @@ object Types { */ def stripAnnots(implicit ctx: Context): Type = this + /** Strip PolyType prefix */ + def stripPoly(implicit ctx: Context): Type = this match { + case tp: PolyType => tp.resType.stripPoly + case _ => this + } + /** Widen from singleton type to its underlying non-singleton * base type by applying one or more `underlying` dereferences, * Also go from => T to T. @@ -1088,43 +1093,38 @@ object Types { } /** The parameter types of a PolyType or MethodType, Empty list for others */ - final def paramTypess(implicit ctx: Context): List[List[Type]] = this match { - case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess - case pt: PolyType => pt.resultType.paramTypess + final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripPoly match { + case mt: MethodType => mt.paramInfos :: mt.resultType.paramInfoss case _ => Nil } /** The parameter names of a PolyType or MethodType, Empty list for others */ - final def paramNamess(implicit ctx: Context): List[List[TermName]] = this match { + final def paramNamess(implicit ctx: Context): List[List[TermName]] = stripPoly match { case mt: MethodType => mt.paramNames :: mt.resultType.paramNamess - case pt: PolyType => pt.resultType.paramNamess case _ => Nil } /** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */ - @tailrec final def firstParamTypes(implicit ctx: Context): List[Type] = this match { - case mt: MethodType => mt.paramTypes - case pt: PolyType => pt.resultType.firstParamTypes + final def firstParamTypes(implicit ctx: Context): List[Type] = stripPoly match { + case mt: MethodType => mt.paramInfos case _ => Nil } /** Is this either not a method at all, or a parameterless method? */ - @tailrec final def isParameterless(implicit ctx: Context): Boolean = this match { + final def isParameterless(implicit ctx: Context): Boolean = stripPoly match { case mt: MethodType => false - case pt: PolyType => pt.resultType.isParameterless case _ => true } - /** The resultType of a PolyType, MethodType, or ExprType, the type itself for others */ + /** The resultType of a LambdaType, or ExprType, the type itself for others */ def resultType(implicit ctx: Context): Type = this /** The final result type of a PolyType, MethodType, or ExprType, after skipping * all parameter sections, the type itself for all others. */ - def finalResultType(implicit ctx: Context): Type = resultType match { + def finalResultType(implicit ctx: Context): Type = resultType.stripPoly match { case mt: MethodType => mt.resultType.finalResultType - case pt: PolyType => pt.resultType.finalResultType case _ => resultType } @@ -1180,8 +1180,8 @@ object Types { final def substDealias(from: List[Symbol], to: List[Type])(implicit ctx: Context): Type = ctx.substDealias(this, from, to, null) - /** Substitute all types of the form `PolyParam(from, N)` by - * `PolyParam(to, N)`. + /** Substitute all types of the form `TypeParamRef(from, N)` by + * `TypeParamRef(to, N)`. */ final def subst(from: BindingType, to: BindingType)(implicit ctx: Context): Type = ctx.subst(this, from, to, null) @@ -1199,7 +1199,7 @@ object Types { ctx.substRecThis(this, binder, tp, null) /** Substitute a bound type by some other type */ - final def substParam(from: ParamType, to: Type)(implicit ctx: Context): Type = + final def substParam(from: ParamRef, to: Type)(implicit ctx: Context): Type = ctx.substParam(this, from, to, null) /** Substitute bound types by some other types */ @@ -1220,7 +1220,7 @@ object Types { */ def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match { case mt: MethodType if !mt.isDependent || ctx.mode.is(Mode.AllowDependentFunctions) => - val formals1 = if (dropLast == 0) mt.paramTypes else mt.paramTypes dropRight dropLast + val formals1 = if (dropLast == 0) mt.paramInfos else mt.paramInfos dropRight dropLast defn.FunctionOf( formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType, mt.isImplicit && !ctx.erasedTypes) } @@ -1360,7 +1360,7 @@ object Types { } /** A marker trait for types that bind other types that refer to them. - * Instances are: PolyType, MethodType, RefinedType. + * Instances are: LambdaType, RecType. */ trait BindingType extends Type @@ -2280,7 +2280,7 @@ object Types { if (tp1 eq tp2) tp1 else apply(tp1, tp2) } - // ----- Method types: MethodType/ExprType/PolyType ------------------------------- + // ----- ExprType and LambdaTypes ----------------------------------- // Note: method types are cached whereas poly types are not. The reason // is that most poly types are cyclic via poly params, @@ -2315,20 +2315,126 @@ object Types { } } - trait MethodOrPoly extends MethodicType + /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */ + abstract case class ExprType(resType: Type) + extends CachedProxyType with TermType with MethodicType { + override def resultType(implicit ctx: Context): Type = resType + override def underlying(implicit ctx: Context): Type = resType + protected def computeSignature(implicit ctx: Context): Signature = resultSignature + def derivedExprType(resType: Type)(implicit ctx: Context) = + if (resType eq this.resType) this else ExprType(resType) + override def computeHash = doHash(resType) + } - abstract case class MethodType(paramNames: List[TermName])( - paramTypesExp: MethodType => List[Type], - resultTypeExp: MethodType => Type) - extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType => - import MethodType._ + final class CachedExprType(resultType: Type) extends ExprType(resultType) + + object ExprType { + def apply(resultType: Type)(implicit ctx: Context) = { + assertUnerased() + unique(new CachedExprType(resultType)) + } + } - def isJava = false + /** The lambda type square: + * + * LambdaType | TermLambda | TypeLambda + * -------------+-------------------+------------------ + * HKLambda | HKTermLambda | HKTypeLambda + * MethodOrPoly | MethodType | PolyType + */ + trait LambdaType extends BindingType with MethodicType { self => + type ThisName <: Name + type PInfo <: Type + type This <: LambdaType{type PInfo = self.PInfo} + + def paramNames: List[ThisName] + def paramInfos: List[PInfo] + def resType: Type + def newParamRef(n: Int): ParamRef + + override def resultType(implicit ctx: Context) = resType + + def isJava: Boolean = false def isImplicit = false - val paramTypes = paramTypesExp(this) - private[core] val resType = resultTypeExp(this) - assert(resType.exists) + def isDependent(implicit ctx: Context): Boolean + def isParamDependent(implicit ctx: Context): Boolean + + final def isTermLambda = isInstanceOf[TermLambda] + final def isTypeLambda = isInstanceOf[TypeLambda] + final def isHigherKinded = isInstanceOf[TypeProxy] + + lazy val paramRefs: List[ParamRef] = paramNames.indices.toList.map(newParamRef) + + protected def computeSignature(implicit ctx: Context) = resultSignature + + final def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = + if (isDependent) resultType.substParams(this, argTypes) + else resultType + + def companion: LambdaTypeCompanion[ThisName, PInfo, This] + + /** The type `[tparams := paramRefs] tp`, where `tparams` can be + * either a list of type parameter symbols or a list of lambda parameters + */ + def integrate(tparams: List[ParamInfo], tp: Type)(implicit ctx: Context): Type = + tparams match { + case LambdaParam(lam, _) :: _ => tp.subst(lam, this) + case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs) + } + + final def derivedLambdaType(paramNames: List[ThisName] = this.paramNames, + paramInfos: List[PInfo] = this.paramInfos, + resType: Type = this.resType)(implicit ctx: Context) = + if ((paramNames eq this.paramNames) && (paramInfos eq this.paramInfos) && (resType eq this.resType)) this + else newLikeThis(paramNames, paramInfos, resType) + + final def newLikeThis(paramNames: List[ThisName], paramInfos: List[PInfo], resType: Type)(implicit ctx: Context): This = + companion(paramNames)( + x => paramInfos.mapConserve(_.subst(this, x).asInstanceOf[PInfo]), + x => resType.subst(this, x)) + + protected def prefixString: String + final override def toString = s"$prefixString($paramNames, $paramInfos, $resType)" + } + + abstract class HKLambda extends CachedProxyType with LambdaType { + final override def underlying(implicit ctx: Context) = resType + + final override def computeHash = doHash(paramNames, resType, paramInfos) + + // Defined here instead of in LambdaType for efficiency + final override def equals(that: Any) = that match { + case that: HKLambda => + this.paramNames == that.paramNames && + this.paramInfos == that.paramInfos && + this.resType == that.resType && + (this.companion eq that.companion) + case _ => + false + } + } + + abstract class MethodOrPoly extends CachedGroundType with LambdaType with TermType { + final override def computeHash = doHash(paramNames, resType, paramInfos) + + // Defined here instead of in LambdaType for efficiency + final override def equals(that: Any) = that match { + case that: MethodOrPoly => + this.paramNames == that.paramNames && + this.paramInfos == that.paramInfos && + this.resType == that.resType && + (this.companion eq that.companion) + case _ => + false + } + } + + trait TermLambda extends LambdaType { thisLambdaType => + import DepStatus._ + type ThisName = TermName + type PInfo = Type + type This <: TermLambda override def resultType(implicit ctx: Context): Type = if (dependencyStatus == FalseDeps) { // dealias all false dependencies @@ -2347,8 +2453,8 @@ object Types { } else resType - var myDependencyStatus: DependencyStatus = Unknown - var myParamDependencyStatus: DependencyStatus = Unknown + private var myDependencyStatus: DependencyStatus = Unknown + private var myParamDependencyStatus: DependencyStatus = Unknown private def depStatus(initial: DependencyStatus, tp: Type)(implicit ctx: Context): DependencyStatus = { def combine(x: DependencyStatus, y: DependencyStatus) = { @@ -2361,7 +2467,7 @@ object Types { if (status == TrueDeps) status else tp match { - case MethodParam(`thisMethodType`, _) => TrueDeps + case TermParamRef(`thisLambdaType`, _) => TrueDeps case tp: TypeRef => val status1 = foldOver(status, tp) tp.info match { // follow type alias to avoid dependency @@ -2401,8 +2507,8 @@ object Types { if (myParamDependencyStatus != Unknown) myParamDependencyStatus else { val result = - if (paramTypes.isEmpty) NoDeps - else (NoDeps /: paramTypes.tail)(depStatus(_, _)) + if (paramInfos.isEmpty) NoDeps + else (NoDeps /: paramInfos.tail)(depStatus(_, _)) if ((result & Provisional) == 0) myParamDependencyStatus = result (result & StatusMask).toByte } @@ -2418,75 +2524,90 @@ object Types { */ def isParamDependent(implicit ctx: Context): Boolean = paramDependencyStatus == TrueDeps - protected def computeSignature(implicit ctx: Context): Signature = - resultSignature.prepend(paramTypes, isJava) + def newParamRef(n: Int) = TermParamRef(this, n) + } - def derivedMethodType(paramNames: List[TermName] = this.paramNames, - paramTypes: List[Type] = this.paramTypes, - resType: Type = this.resType)(implicit ctx: Context) = - if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this - else { - val paramTypesFn = (x: MethodType) => paramTypes.map(_.subst(this, x)) - val resTypeFn = (x: MethodType) => resType.subst(this, x) - if (isJava) JavaMethodType(paramNames)(paramTypesFn, resTypeFn) - else if (isImplicit) ImplicitMethodType(paramNames)(paramTypesFn, resTypeFn) - else MethodType(paramNames)(paramTypesFn, resTypeFn) - } + abstract case class MethodType(paramNames: List[TermName])( + paramInfosExp: MethodType => List[Type], + resultTypeExp: MethodType => Type) + extends MethodOrPoly with TermLambda with NarrowCached { thisMethodType => + import MethodType._ - def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = - if (isDependent) resultType.substParams(this, argTypes) - else resultType + type This = MethodType - override def equals(that: Any) = that match { - case that: MethodType => - this.paramNames == that.paramNames && - this.paramTypes == that.paramTypes && - this.resType == that.resType - case _ => - false - } + val paramInfos = paramInfosExp(this) + val resType = resultTypeExp(this) + assert(resType.exists) - override def computeHash = doHash(paramNames, resType, paramTypes) + override def computeSignature(implicit ctx: Context): Signature = + resultSignature.prepend(paramInfos, isJava) protected def prefixString = "MethodType" - override def toString = s"$prefixString($paramNames, $paramTypes, $resType)" } - final class CachedMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) - extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[CachedMethodType] + final class CachedMethodType(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramInfosExp, resultTypeExp) { + def companion = MethodType } - final class JavaMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) - extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { + final class JavaMethodType(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramInfosExp, resultTypeExp) { + def companion = JavaMethodType override def isJava = true - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[JavaMethodType] - override def computeHash = addDelta(super.computeHash, 1) override protected def prefixString = "JavaMethodType" } - final class ImplicitMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) - extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { + final class ImplicitMethodType(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramInfosExp, resultTypeExp) { + def companion = ImplicitMethodType override def isImplicit = true - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[ImplicitMethodType] - override def computeHash = addDelta(super.computeHash, 2) override protected def prefixString = "ImplicitMethodType" } - abstract class MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType - def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = - apply(paramNames)(_ => paramTypes, _ => resultType) - def apply(paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - apply(nme.syntheticParamNames(paramTypes.length))(_ => paramTypes, resultTypeExp) - def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = - apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) + abstract class LambdaTypeCompanion[N <: Name, PInfo <: Type, LT <: LambdaType] { + def syntheticParamName(n: Int): N + + @sharable private val memoizedNames = new mutable.HashMap[Int, List[N]] + def syntheticParamNames(n: Int): List[N] = synchronized { + memoizedNames.getOrElseUpdate(n, (0 until n).map(syntheticParamName).toList) + } + + def apply(paramNames: List[N])(paramInfosExp: LT => List[PInfo], resultTypeExp: LT => Type)(implicit ctx: Context): LT + def apply(paramNames: List[N], paramInfos: List[PInfo], resultType: Type)(implicit ctx: Context): LT = + apply(paramNames)(_ => paramInfos, _ => resultType) + def apply(paramInfos: List[PInfo])(resultTypeExp: LT => Type)(implicit ctx: Context): LT = + apply(syntheticParamNames(paramInfos.length))(_ => paramInfos, resultTypeExp) + def apply(paramInfos: List[PInfo], resultType: Type)(implicit ctx: Context): LT = + apply(syntheticParamNames(paramInfos.length), paramInfos, resultType) + + protected def paramName(param: ParamInfo.Of[N])(implicit ctx: Context): N = + param.paramName + + def fromParams[PI <: ParamInfo.Of[N]](params: List[PI], resultType: Type)(implicit ctx: Context): Type = + if (params.isEmpty) resultType + else apply(params.map(paramName))( + tl => params.map(param => tl.integrate(params, param.paramInfo).asInstanceOf[PInfo]), + tl => tl.integrate(params, resultType)) + } + + abstract class TermLambdaCompanion[LT <: TermLambda] + extends LambdaTypeCompanion[TermName, Type, LT] { + def syntheticParamName(n: Int) = nme.syntheticParamName(n) + } + + abstract class TypeLambdaCompanion[LT <: TypeLambda] + extends LambdaTypeCompanion[TypeName, TypeBounds, LT] { + def syntheticParamName(n: Int) = tpnme.syntheticTypeParamName(n) + } + + abstract class MethodTypeCompanion extends TermLambdaCompanion[MethodType] { /** Produce method type from parameter symbols, with special mappings for repeated - * and inline parameters. + * and inline parameters: + * - replace @repeated annotations on Seq or Array types by <repeated> types + * - add @inlineParam to inline call-by-value parameters */ def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { - /** Replace @repeated annotations on Seq or Array types by <repeated> types */ def translateRepeated(tp: Type): Type = tp match { case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1)) case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot => @@ -2496,27 +2617,25 @@ object Types { case tp => tp } - /** Add @inlineParam to inline call-by-value parameters */ def translateInline(tp: Type): Type = tp match { case _: ExprType => tp case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot)) } - def integrate(tp: Type, mt: MethodType) = - tp.subst(params, (0 until params.length).toList.map(MethodParam(mt, _))) - def paramInfo(param: Symbol): Type = { + def paramInfo(param: Symbol) = { val paramType = translateRepeated(param.info) if (param.is(Inline)) translateInline(paramType) else paramType } + apply(params.map(_.name.asTermName))( - mt => params.map(param => integrate(paramInfo(param), mt)), - mt => integrate(resultType, mt)) + tl => params.map(p => tl.integrate(params, paramInfo(p))), + tl => tl.integrate(params, resultType)) } def checkValid(mt: MethodType)(implicit ctx: Context): mt.type = { if (Config.checkMethodTypes) - for ((paramType, idx) <- mt.paramTypes.zipWithIndex) - paramType.foreachPart { - case MethodParam(`mt`, j) => assert(j < idx, mt) + for ((paramInfo, idx) <- mt.paramInfos.zipWithIndex) + paramInfo.foreachPart { + case TermParamRef(`mt`, j) => assert(j < idx, mt) case _ => } mt @@ -2524,114 +2643,97 @@ object Types { } object MethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - checkValid(unique(new CachedMethodType(paramNames)(paramTypesExp, resultTypeExp))) - - private type DependencyStatus = Byte - private final val Unknown: DependencyStatus = 0 // not yet computed - private final val NoDeps: DependencyStatus = 1 // no dependent parameters found - private final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types - private final val TrueDeps: DependencyStatus = 3 // some truly dependent parameters exist - private final val StatusMask: DependencyStatus = 3 // the bits indicating actual dependency status - private final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations + def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + checkValid(unique(new CachedMethodType(paramNames)(paramInfosExp, resultTypeExp))) } object JavaMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - unique(new JavaMethodType(paramNames)(paramTypesExp, resultTypeExp)) + def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + unique(new JavaMethodType(paramNames)(paramInfosExp, resultTypeExp)) } object ImplicitMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - checkValid(unique(new ImplicitMethodType(paramNames)(paramTypesExp, resultTypeExp))) + def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + checkValid(unique(new ImplicitMethodType(paramNames)(paramInfosExp, resultTypeExp))) } /** A ternary extractor for MethodType */ object MethodTpe { def unapply(mt: MethodType)(implicit ctx: Context) = - Some((mt.paramNames, mt.paramTypes, mt.resultType)) - } - - /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */ - abstract case class ExprType(resType: Type) - extends CachedProxyType with TermType with MethodicType { - override def resultType(implicit ctx: Context): Type = resType - override def underlying(implicit ctx: Context): Type = resType - protected def computeSignature(implicit ctx: Context): Signature = resultSignature - def derivedExprType(resType: Type)(implicit ctx: Context) = - if (resType eq this.resType) this else ExprType(resType) - override def computeHash = doHash(resType) + Some((mt.paramNames, mt.paramInfos, mt.resultType)) } - final class CachedExprType(resultType: Type) extends ExprType(resultType) - - object ExprType { - def apply(resultType: Type)(implicit ctx: Context) = { - assertUnerased() - unique(new CachedExprType(resultType)) - } - } - - /** A type lambda of the form `[v_0 X_0, ..., v_n X_n] => T` */ - class PolyType(val paramNames: List[TypeName], val variances: List[Int])( - paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type) - extends CachedProxyType with BindingType with MethodOrPoly { - - /** The bounds of the type parameters */ - val paramBounds: List[TypeBounds] = paramBoundsExp(this) - - /** The result type of a PolyType / body of a type lambda */ - val resType: Type = resultTypeExp(this) - - assert(resType.isInstanceOf[TermType], this) - assert(paramNames.nonEmpty) - - protected def computeSignature(implicit ctx: Context) = resultSignature + trait TypeLambda extends LambdaType { + type ThisName = TypeName + type PInfo = TypeBounds + type This <: TypeLambda - def isPolymorphicMethodType: Boolean = resType match { - case _: MethodType => true - case _ => false - } + def isDependent(implicit ctx: Context): Boolean = true + def isParamDependent(implicit ctx: Context): Boolean = true - /** PolyParam references to all type parameters of this type */ - lazy val paramRefs: List[PolyParam] = paramNames.indices.toList.map(PolyParam(this, _)) + def newParamRef(n: Int) = TypeParamRef(this, n) lazy val typeParams: List[LambdaParam] = paramNames.indices.toList.map(new LambdaParam(this, _)) - override def resultType(implicit ctx: Context) = resType - override def underlying(implicit ctx: Context) = resType - - /** Instantiate result type by substituting parameters with given arguments */ - final def instantiate(argTypes: List[Type])(implicit ctx: Context): Type = - resultType.substParams(this, argTypes) - /** Instantiate parameter bounds by substituting parameters with given arguments */ - final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] = - paramBounds.mapConserve(_.substParams(this, argTypes).bounds) - - def newLikeThis(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType = - PolyType.apply(paramNames, variances)( - x => paramBounds mapConserve (_.subst(this, x).bounds), - x => resType.subst(this, x)) - - def derivedPolyType(paramNames: List[TypeName] = this.paramNames, - paramBounds: List[TypeBounds] = this.paramBounds, - resType: Type = this.resType)(implicit ctx: Context) = - if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (resType eq this.resType)) this - else newLikeThis(paramNames, paramBounds, resType) + final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[Type] = + paramInfos.mapConserve(_.substParams(this, argTypes)) - def derivedLambdaAbstraction(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type = + def derivedLambdaAbstraction(paramNames: List[TypeName], paramInfos: List[TypeBounds], resType: Type)(implicit ctx: Context): Type = resType match { case resType @ TypeAlias(alias) => - resType.derivedTypeAlias(newLikeThis(paramNames, paramBounds, alias)) + resType.derivedTypeAlias(newLikeThis(paramNames, paramInfos, alias)) case resType @ TypeBounds(lo, hi) => resType.derivedTypeBounds( - if (lo.isRef(defn.NothingClass)) lo else newLikeThis(paramNames, paramBounds, lo), - newLikeThis(paramNames, paramBounds, hi)) + if (lo.isRef(defn.NothingClass)) lo else newLikeThis(paramNames, paramInfos, lo), + newLikeThis(paramNames, paramInfos, hi)) case _ => - derivedPolyType(paramNames, paramBounds, resType) + derivedLambdaType(paramNames, paramInfos, resType) } + } + + /** A type lambda of the form `[X_0 B_0, ..., X_n B_n] => T` + * Variances are encoded in parameter names. A name starting with `+` + * designates a covariant parameter, a name starting with `-` designates + * a contravariant parameter, and every other name designates a non-variant parameter. + * + * @param paramNames The names `X_0`, ..., `X_n` + * @param paramInfosExp A function that, given the polytype itself, returns the + * parameter bounds `B_1`, ..., `B_n` + * @param resultTypeExp A function that, given the polytype itself, returns the + * result type `T`. + */ + class HKTypeLambda(val paramNames: List[TypeName])( + paramInfosExp: HKTypeLambda => List[TypeBounds], resultTypeExp: HKTypeLambda => Type) + extends HKLambda with TypeLambda { + type This = HKTypeLambda + def companion = HKTypeLambda + + val paramInfos: List[TypeBounds] = paramInfosExp(this) + val resType: Type = resultTypeExp(this) + + assert(resType.isInstanceOf[TermType], this) + assert(paramNames.nonEmpty) + + protected def prefixString = "HKTypeLambda" + } + + /** The type of a polymorphic method. It has the same form as HKTypeLambda, + * except it applies to terms and parameters do not have variances. + */ + class PolyType(val paramNames: List[TypeName])( + paramInfosExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type) + extends MethodOrPoly with TypeLambda { + + type This = PolyType + def companion = PolyType + + val paramInfos: List[TypeBounds] = paramInfosExp(this) + val resType: Type = resultTypeExp(this) + + assert(resType.isInstanceOf[TermType], this) + assert(paramNames.nonEmpty) /** Merge nested polytypes into one polytype. nested polytypes are normally not supported * but can arise as temporary data structures. @@ -2640,67 +2742,95 @@ object Types { case that: PolyType => val shift = new TypeMap { def apply(t: Type) = t match { - case PolyParam(`that`, n) => PolyParam(that, n + paramNames.length) + case TypeParamRef(`that`, n) => TypeParamRef(that, n + paramNames.length) case t => mapOver(t) } } - PolyType(paramNames ++ that.paramNames, variances ++ that.variances)( - x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++ - that.paramBounds.mapConserve(shift(_).subst(that, x).bounds), + PolyType(paramNames ++ that.paramNames)( + x => this.paramInfos.mapConserve(_.subst(this, x).bounds) ++ + that.paramInfos.mapConserve(shift(_).subst(that, x).bounds), x => shift(that.resultType).subst(that, x).subst(this, x)) case _ => this } - /** The type `[tparams := paramRefs] tp`, where `tparams` can be - * either a list of type parameter symbols or a list of lambda parameters - */ - def lifted(tparams: List[TypeParamInfo], tp: Type)(implicit ctx: Context): Type = - tparams match { - case LambdaParam(poly, _) :: _ => tp.subst(poly, this) - case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs) - } + protected def prefixString = "PolyType" + } - override def equals(other: Any) = other match { - case other: PolyType => - other.paramNames == this.paramNames && - other.paramBounds == this.paramBounds && - other.resType == this.resType && - other.variances == this.variances - case _ => false + object HKTypeLambda extends TypeLambdaCompanion[HKTypeLambda] { + def apply(paramNames: List[TypeName])( + paramInfosExp: HKTypeLambda => List[TypeBounds], + resultTypeExp: HKTypeLambda => Type)(implicit ctx: Context): HKTypeLambda = { + unique(new HKTypeLambda(paramNames)(paramInfosExp, resultTypeExp)) } - override def toString = s"PolyType($variances, $paramNames, $paramBounds, $resType)" + def unapply(tl: HKTypeLambda): Some[(List[LambdaParam], Type)] = + Some((tl.typeParams, tl.resType)) + + def any(n: Int)(implicit ctx: Context) = + apply(syntheticParamNames(n))( + pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) - override def computeHash = doHash(variances ::: paramNames, resType, paramBounds) + override def paramName(param: ParamInfo.Of[TypeName])(implicit ctx: Context): TypeName = + param.paramName.withVariance(param.paramVariance) + + /** Distributes Lambda inside type bounds. Examples: + * + * type T[X] = U becomes type T = [X] -> U + * type T[X] <: U becomes type T >: Nothign <: ([X] -> U) + * type T[X] >: L <: U becomes type T >: ([X] -> L) <: ([X] -> U) + */ + override def fromParams[PI <: ParamInfo.Of[TypeName]](params: List[PI], resultType: Type)(implicit ctx: Context): Type = { + def expand(tp: Type) = super.fromParams(params, tp) + resultType match { + case rt: TypeAlias => + rt.derivedTypeAlias(expand(rt.alias)) + case rt @ TypeBounds(lo, hi) => + rt.derivedTypeBounds( + if (lo.isRef(defn.NothingClass)) lo else expand(lo), expand(hi)) + case rt => + expand(rt) + } + } } - object PolyType { - def apply(paramNames: List[TypeName], variances: List[Int])( - paramBoundsExp: PolyType => List[TypeBounds], + object PolyType extends TypeLambdaCompanion[PolyType] { + def apply(paramNames: List[TypeName])( + paramInfosExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type)(implicit ctx: Context): PolyType = { - unique(new PolyType(paramNames, variances)(paramBoundsExp, resultTypeExp)) + unique(new PolyType(paramNames)(paramInfosExp, resultTypeExp)) } def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] = Some((tl.typeParams, tl.resType)) def any(n: Int)(implicit ctx: Context) = - apply(tpnme.syntheticTypeParamNames(n), List.fill(n)(0))( + apply(syntheticParamNames(n))( pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) } + private object DepStatus { + type DependencyStatus = Byte + final val Unknown: DependencyStatus = 0 // not yet computed + final val NoDeps: DependencyStatus = 1 // no dependent parameters found + final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types + final val TrueDeps: DependencyStatus = 3 // some truly dependent parameters exist + final val StatusMask: DependencyStatus = 3 // the bits indicating actual dependency status + final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations + } + // ----- HK types: LambdaParam, HKApply --------------------- /** The parameter of a type lambda */ - case class LambdaParam(tl: PolyType, n: Int) extends TypeParamInfo { - def isTypeParam(implicit ctx: Context) = true - def paramName(implicit ctx: Context): TypeName = tl.paramNames(n) - def paramBounds(implicit ctx: Context): TypeBounds = tl.paramBounds(n) - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds = paramBounds - def paramBoundsOrCompleter(implicit ctx: Context): Type = paramBounds - def paramVariance(implicit ctx: Context): Int = tl.variances(n) - def toArg: Type = PolyParam(tl, n) - def paramRef(implicit ctx: Context): Type = PolyParam(tl, n) + case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo { + type ThisName = TypeName + def isTypeParam(implicit ctx: Context) = tl.paramNames.head.isTypeName + def paramName(implicit ctx: Context) = tl.paramNames(n) + def paramInfo(implicit ctx: Context) = tl.paramInfos(n) + def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = paramInfo + def paramInfoOrCompleter(implicit ctx: Context): Type = paramInfo + def paramVariance(implicit ctx: Context): Int = tl.paramNames(n).variance + def toArg: Type = TypeParamRef(tl, n) + def paramRef(implicit ctx: Context): Type = TypeParamRef(tl, n) } /** A higher kinded type application `C[T_1, ..., T_n]` */ @@ -2715,7 +2845,7 @@ object Types { override def superType(implicit ctx: Context): Type = { if (ctx.period != validSuper) { cachedSuper = tycon match { - case tp: PolyType => defn.AnyType + case tp: HKTypeLambda => defn.AnyType case tp: TypeVar if !tp.inst.exists => // supertype not stable, since underlying might change return tp.underlying.applyIfParameterized(args) @@ -2739,9 +2869,9 @@ object Types { NoType } - def typeParams(implicit ctx: Context): List[TypeParamInfo] = { + def typeParams(implicit ctx: Context): List[ParamInfo] = { val tparams = tycon.typeParams - if (tparams.isEmpty) PolyType.any(args.length).typeParams else tparams + if (tparams.isEmpty) HKTypeLambda.any(args.length).typeParams else tparams } def derivedAppliedType(tycon: Type, args: List[Type])(implicit ctx: Context): Type = @@ -2753,8 +2883,8 @@ object Types { protected def checkInst(implicit ctx: Context): this.type = { def check(tycon: Type): Unit = tycon.stripTypeVar match { case tycon: TypeRef if !tycon.symbol.isClass => - case _: PolyParam | _: ErrorType | _: WildcardType => - case _: PolyType => + case _: TypeParamRef | _: ErrorType | _: WildcardType => + case _: TypeLambda => assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this") case tycon: AnnotatedType => check(tycon.underlying) @@ -2773,92 +2903,61 @@ object Types { unique(new CachedHKApply(tycon, args)).checkInst } - // ----- Bound types: MethodParam, PolyParam -------------------------- + // ----- BoundTypes: ParamRef, RecThis ---------------------------------------- abstract class BoundType extends CachedProxyType with ValueType { type BT <: Type - def binder: BT - // Dotty deviation: copyBoundType was copy, but - // dotty generates copy methods always automatically, and therefore - // does not accept same-named method definitions in subclasses. - // Scala2x, on the other hand, requires them (not sure why!) + val binder: BT def copyBoundType(bt: BT): Type } - abstract class ParamType extends BoundType { + abstract class ParamRef extends BoundType { + type BT <: LambdaType def paramNum: Int - def paramName: Name - } - - abstract case class MethodParam(binder: MethodType, paramNum: Int) extends ParamType with SingletonType { - type BT = MethodType - - def paramName = binder.paramNames(paramNum) + def paramName: binder.ThisName = binder.paramNames(paramNum) - override def underlying(implicit ctx: Context): Type = binder.paramTypes(paramNum) - def copyBoundType(bt: BT) = new MethodParamImpl(bt, paramNum) + override def underlying(implicit ctx: Context): Type = { + val infos = binder.paramInfos + if (infos == null) NoType // this can happen if the referenced generic type is not initialized yet + else infos(paramNum) + } - // need to customize hashCode and equals to prevent infinite recursion for dep meth types. - override def computeHash = addDelta(binder.identityHash, paramNum) + override def computeHash = doHash(paramNum, binder.identityHash) override def equals(that: Any) = that match { - case that: MethodParam => + case that: ParamRef => (this.binder eq that.binder) && this.paramNum == that.paramNum case _ => false } - override def toString = s"MethodParam($paramName)" + override def toString = + try s"ParamRef($paramName)" + catch { + case ex: IndexOutOfBoundsException => s"ParamRef(<bad index: $paramNum>)" + } } - class MethodParamImpl(binder: MethodType, paramNum: Int) extends MethodParam(binder, paramNum) - - object MethodParam { - def apply(binder: MethodType, paramNum: Int)(implicit ctx: Context): MethodParam = { - assertUnerased() - new MethodParamImpl(binder, paramNum) - } + case class TermParamRef(binder: TermLambda, paramNum: Int) extends ParamRef { + type BT = TermLambda + def copyBoundType(bt: BT) = TermParamRef(bt, paramNum) } - /** TODO Some docs would be nice here! */ - case class PolyParam(binder: PolyType, paramNum: Int) extends ParamType { - type BT = PolyType - def copyBoundType(bt: BT) = PolyParam(bt, paramNum) + case class TypeParamRef(binder: TypeLambda, paramNum: Int) extends ParamRef { + type BT = TypeLambda + def copyBoundType(bt: BT) = TypeParamRef(bt, paramNum) /** Looking only at the structure of `bound`, is one of the following true? * - fromBelow and param <:< bound * - !fromBelow and param >:> bound */ def occursIn(bound: Type, fromBelow: Boolean)(implicit ctx: Context): Boolean = bound.stripTypeVar match { - case bound: PolyParam => bound == this + case bound: ParamRef => bound == this case bound: AndOrType => def occ1 = occursIn(bound.tp1, fromBelow) def occ2 = occursIn(bound.tp2, fromBelow) if (fromBelow == bound.isAnd) occ1 && occ2 else occ1 || occ2 case _ => false } - - def paramName = binder.paramNames(paramNum) - - override def underlying(implicit ctx: Context): Type = { - val bounds = binder.paramBounds - if (bounds == null) NoType // this can happen if the referenced generic type is not initialized yet - else bounds(paramNum) - } - // no customized hashCode/equals needed because cycle is broken in PolyType - override def toString = - try s"PolyParam($paramName)" - catch { - case ex: IndexOutOfBoundsException => s"PolyParam(<bad index: $paramNum>)" - } - - override def computeHash = doHash(paramNum, binder.identityHash) - - override def equals(that: Any) = that match { - case that: PolyParam => - (this.binder eq that.binder) && this.paramNum == that.paramNum - case _ => - false - } } /** a self-reference to an enclosing recursive type. */ @@ -2929,7 +3028,7 @@ object Types { * `owningTree` and `owner` are used to determine whether a type-variable can be instantiated * at some given point. See `Inferencing#interpolateUndetVars`. */ - final class TypeVar(val origin: PolyParam, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType { + final class TypeVar(val origin: TypeParamRef, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType { /** The permanent instance type of the variable, or NoType is none is given yet */ private[core] var inst: Type = NoType @@ -3341,9 +3440,8 @@ object Types { object SAMType { def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match { case tp: ClassInfo => - def zeroParams(tp: Type): Boolean = tp match { - case pt: PolyType => zeroParams(pt.resultType) - case mt: MethodType => mt.paramTypes.isEmpty && !mt.resultType.isInstanceOf[MethodType] + def zeroParams(tp: Type): Boolean = tp.stripPoly match { + case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType] case et: ExprType => true case _ => false } @@ -3426,12 +3524,11 @@ object Types { tp.derivedClassInfo(pre) protected def derivedJavaArrayType(tp: JavaArrayType, elemtp: Type): Type = tp.derivedJavaArrayType(elemtp) - protected def derivedMethodType(tp: MethodType, formals: List[Type], restpe: Type): Type = - tp.derivedMethodType(tp.paramNames, formals, restpe) protected def derivedExprType(tp: ExprType, restpe: Type): Type = tp.derivedExprType(restpe) - protected def derivedPolyType(tp: PolyType, pbounds: List[TypeBounds], restpe: Type): Type = - tp.derivedPolyType(tp.paramNames, pbounds, restpe) + // note: currying needed because Scala2 does not support param-dependencies + protected def derivedLambdaType(tp: LambdaType)(formals: List[tp.PInfo], restpe: Type): Type = + tp.derivedLambdaType(tp.paramNames, formals, restpe) /** Map this function over given type */ def mapOver(tp: Type): Type = { @@ -3461,29 +3558,34 @@ object Types { variance = -variance derivedTypeBounds(tp, lo1, this(tp.hi)) - case tp: MethodType => - def mapOverMethod = { - variance = -variance - val ptypes1 = tp.paramTypes mapConserve this - variance = -variance - derivedMethodType(tp, ptypes1, this(tp.resultType)) + case tp: RecType => + derivedRecType(tp, this(tp.parent)) + + case tp: TypeVar => + val inst = tp.instanceOpt + if (inst.exists) apply(inst) else tp + + case tp: HKApply => + def mapArg(arg: Type, tparam: ParamInfo): Type = { + val saved = variance + variance *= tparam.paramVariance + try this(arg) + finally variance = saved } - mapOverMethod + derivedAppliedType(tp, this(tp.tycon), + tp.args.zipWithConserve(tp.typeParams)(mapArg)) case tp: ExprType => derivedExprType(tp, this(tp.resultType)) - case tp: PolyType => - def mapOverPoly = { + case tp: LambdaType => + def mapOverLambda = { variance = -variance - val bounds1 = tp.paramBounds.mapConserve(this).asInstanceOf[List[TypeBounds]] + val ptypes1 = tp.paramInfos.mapConserve(this).asInstanceOf[List[tp.PInfo]] variance = -variance - derivedPolyType(tp, bounds1, this(tp.resultType)) + derivedLambdaType(tp)(ptypes1, this(tp.resultType)) } - mapOverPoly - - case tp: RecType => - derivedRecType(tp, this(tp.parent)) + mapOverLambda case tp @ SuperType(thistp, supertp) => derivedSuperType(tp, this(thistp), this(supertp)) @@ -3494,21 +3596,7 @@ object Types { case tp: ClassInfo => mapClassInfo(tp) - case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) apply(inst) else tp - - case tp: HKApply => - def mapArg(arg: Type, tparam: TypeParamInfo): Type = { - val saved = variance - variance *= tparam.paramVariance - try this(arg) - finally variance = saved - } - derivedAppliedType(tp, this(tp.tycon), - tp.args.zipWithConserve(tp.typeParams)(mapArg)) - - case tp: AndOrType => + case tp: AndOrType => derivedAndOrType(tp, this(tp.tp1), this(tp.tp2)) case tp: SkolemType => @@ -3689,23 +3777,14 @@ object Types { this(y, hi) } - case tp: MethodType => - variance = -variance - val y = foldOver(x, tp.paramTypes) - variance = -variance - this(y, tp.resultType) + case tp: RecType => + this(x, tp.parent) case ExprType(restpe) => this(x, restpe) - case tp: PolyType => - variance = -variance - val y = foldOver(x, tp.paramBounds) - variance = -variance - this(y, tp.resultType) - - case tp: RecType => - this(x, tp.parent) + case tp: TypeVar => + this(x, tp.underlying) case SuperType(thistp, supertp) => this(this(x, thistp), supertp) @@ -3714,7 +3793,7 @@ object Types { this(x, prefix) case tp @ HKApply(tycon, args) => - @tailrec def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T = + @tailrec def foldArgs(x: T, tparams: List[ParamInfo], args: List[Type]): T = if (args.isEmpty) { assert(tparams.isEmpty) x @@ -3730,6 +3809,12 @@ object Types { } foldArgs(this(x, tycon), tp.typeParams, args) + case tp: LambdaType => + variance = -variance + val y = foldOver(x, tp.paramInfos) + variance = -variance + this(y, tp.resultType) + case tp: AndOrType => this(this(x, tp.tp1), tp.tp2) @@ -3739,9 +3824,6 @@ object Types { case AnnotatedType(underlying, annot) => this(applyToAnnot(x, annot), underlying) - case tp: TypeVar => - this(x, tp.underlying) - case tp: WildcardType => this(x, tp.optBounds) @@ -3804,9 +3886,7 @@ object Types { apply(x, tp.tref) case tp: ConstantType => apply(x, tp.underlying) - case tp: MethodParam => - apply(x, tp.underlying) - case tp: PolyParam => + case tp: ParamRef => apply(x, tp.underlying) case _ => foldOver(x, tp) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index e0b233ce8..da875c906 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -199,7 +199,7 @@ class ClassfileParser( def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match { case Some(entry) if !isStatic(entry.jflags) => val mt @ MethodTpe(paramNames, paramTypes, resultType) = denot.info - denot.info = mt.derivedMethodType(paramNames.tail, paramTypes.tail, resultType) + denot.info = mt.derivedLambdaType(paramNames.tail, paramTypes.tail, resultType) case _ => } @@ -209,7 +209,7 @@ class ClassfileParser( def normalizeConstructorInfo() = { val mt @ MethodType(paramNames) = denot.info val rt = classRoot.typeRef appliedTo (classRoot.typeParams map (_.typeRef)) - denot.info = mt.derivedMethodType(paramNames, mt.paramTypes, rt) + denot.info = mt.derivedLambdaType(paramNames, mt.paramInfos, rt) addConstructorTypeParams(denot) } @@ -975,7 +975,7 @@ class ClassfileParser( if (name == nme.CONSTRUCTOR) tpe match { case tp: MethodType => - tp.derivedMethodType(tp.paramNames, tp.paramTypes, ownerTpe) + tp.derivedLambdaType(tp.paramNames, tp.paramInfos, ownerTpe) } p = (name, tpe) values(index) = p diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index cb1b56c3c..8b2255e94 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -151,8 +151,9 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef bounds_Type // for type-variables defined in a type pattern BYNAMEtype underlying_Type - POLYtype Length result_Type NamesTypes // variance encoded in front of name: +/-/= + POLYtype Length result_Type NamesTypes METHODtype Length result_Type NamesTypes // needed for refinements + TYPELAMBDAtype Length result_Type NamesTypes // variance encoded in front of name: +/-/(nothing) PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements SHARED type_ASTRef NamesTypes = NameType* @@ -345,9 +346,10 @@ object TastyFormat { final val ORtpt = 169 final val METHODtype = 170 final val POLYtype = 171 - final val POLYtpt = 172 - final val PARAMtype = 173 - final val ANNOTATION = 174 + final val TYPELAMBDAtype = 172 + final val LAMBDAtpt = 173 + final val PARAMtype = 174 + final val ANNOTATION = 175 final val firstSimpleTreeTag = UNITconst final val firstNatTreeTag = SHARED @@ -397,7 +399,7 @@ object TastyFormat { | SINGLETONtpt | REFINEDtpt | APPLIEDtpt - | POLYtpt + | LAMBDAtpt | TYPEBOUNDStpt | ANNOTATEDtpt | ANDtpt @@ -528,8 +530,9 @@ object TastyFormat { case BYNAMEtype => "BYNAMEtype" case BYNAMEtpt => "BYNAMEtpt" case POLYtype => "POLYtype" - case POLYtpt => "POLYtpt" case METHODtype => "METHODtype" + case TYPELAMBDAtype => "TYPELAMBDAtype" + case LAMBDAtpt => "LAMBDAtpt" case PARAMtype => "PARAMtype" case ANNOTATION => "ANNOTATION" case PRIVATEqualified => "PRIVATEqualified" @@ -543,11 +546,7 @@ object TastyFormat { case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | SELFDEF | REFINEDtype => 1 case RENAMED | PARAMtype => 2 - case POLYtype | METHODtype => -1 + case POLYtype | METHODtype | TYPELAMBDAtype => -1 case _ => 0 } - - /** Map between variances and name prefixes */ - val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+') - val prefixToVariance = Map('-' -> -1, '=' -> 0, '+' -> 1) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index fb37c9e7d..ce3722ff1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -77,7 +77,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { printName(); printTree(); printTrees() case RETURN => printNat(); printTrees() - case METHODtype | POLYtype => + case METHODtype | POLYtype | TYPELAMBDAtype => printTree() until(end) { printName(); printTree() } case PARAMtype => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 80270aa25..902d01c21 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -253,23 +253,21 @@ class TreePickler(pickler: TastyPickler) { case tpe: ExprType => writeByte(BYNAMEtype) pickleType(tpe.underlying) - case tpe: PolyType => - writeByte(POLYtype) - val paramNames = tpe.typeParams.map(tparam => - varianceToPrefix(tparam.paramVariance) +: tparam.paramName) - pickleMethodic(tpe.resultType, paramNames, tpe.paramBounds) + case tpe: HKTypeLambda => + pickleMethodic(TYPELAMBDAtype, tpe) + case tpe: PolyType if richTypes => + pickleMethodic(POLYtype, tpe) case tpe: MethodType if richTypes => - writeByte(METHODtype) - pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramTypes) - case tpe: PolyParam => - if (!pickleParamType(tpe)) + pickleMethodic(METHODtype, tpe) + case tpe: TypeParamRef => + if (!pickleParamRef(tpe)) // TODO figure out why this case arises in e.g. pickling AbstractFileReader. ctx.typerState.constraint.entry(tpe) match { case TypeBounds(lo, hi) if lo eq hi => pickleNewType(lo, richTypes) case _ => assert(false, s"orphan poly parameter: $tpe") } - case tpe: MethodParam => - assert(pickleParamType(tpe), s"orphan method parameter: $tpe") + case tpe: TermParamRef => + assert(pickleParamRef(tpe), s"orphan method parameter: $tpe") case tpe: LazyRef => pickleType(tpe.ref) }} catch { @@ -283,15 +281,17 @@ class TreePickler(pickler: TastyPickler) { pickleName(qualifiedName(pkg)) } - def pickleMethodic(result: Type, names: List[Name], types: List[Type])(implicit ctx: Context) = + def pickleMethodic(tag: Int, tpe: LambdaType)(implicit ctx: Context) = { + writeByte(tag) withLength { - pickleType(result, richTypes = true) - (names, types).zipped.foreach { (name, tpe) => + pickleType(tpe.resultType, richTypes = true) + (tpe.paramNames, tpe.paramInfos).zipped.foreach { (name, tpe) => pickleName(name); pickleType(tpe) } } + } - def pickleParamType(tpe: ParamType)(implicit ctx: Context): Boolean = { + def pickleParamRef(tpe: ParamRef)(implicit ctx: Context): Boolean = { val binder = pickledTypes.get(tpe.binder) val pickled = binder != null if (pickled) { @@ -555,8 +555,8 @@ class TreePickler(pickler: TastyPickler) { case Annotated(tree, annot) => writeByte(ANNOTATEDtpt) withLength { pickleTree(tree); pickleTree(annot.tree) } - case PolyTypeTree(tparams, body) => - writeByte(POLYtpt) + case LambdaTypeTree(tparams, body) => + writeByte(LAMBDAtpt) withLength { pickleParams(tparams); pickleTree(body) } case TypeBoundsTree(lo, hi) => writeByte(TYPEBOUNDStpt) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 88b6eef7a..4db995e10 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -38,7 +38,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle /** A map from addresses of type entries to the types they define. * Currently only populated for types that might be recursively referenced - * from within themselves (i.e. RefinedTypes, PolyTypes, MethodTypes). + * from within themselves (i.e. RecTypes, LambdaTypes). */ private val typeAtAddr = new mutable.HashMap[Addr, Type] @@ -227,11 +227,17 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readLengthType(): Type = { val end = readEnd() - def readNamesSkipParams: (List[Name], TreeReader) = { + def readMethodic[N <: Name, PInfo <: Type, LT <: LambdaType] + (companion: LambdaTypeCompanion[N, PInfo, LT], nameMap: Name => N): LT = { val nameReader = fork nameReader.skipTree() // skip result val paramReader = nameReader.fork - (nameReader.readParamNames(end), paramReader) + val paramNames = nameReader.readParamNames(end).map(nameMap) + val result = companion(paramNames)( + pt => registeringType(pt, paramReader.readParamTypes[PInfo](end)), + pt => readType()) + goto(end) + result } val result = @@ -268,25 +274,14 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle registerSym(start, sym) TypeRef.withFixedSym(NoPrefix, sym.name, sym) case POLYtype => - val (rawNames, paramReader) = readNamesSkipParams - val (variances, paramNames) = rawNames - .map(name => (prefixToVariance(name.head), name.tail.toTypeName)).unzip - val result = PolyType(paramNames, variances)( - pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)), - pt => readType()) - goto(end) - result + readMethodic(PolyType, _.toTypeName) case METHODtype => - val (names, paramReader) = readNamesSkipParams - val result = MethodType(names.map(_.toTermName))( - mt => registeringType(mt, paramReader.readParamTypes[Type](end)), - mt => readType()) - goto(end) - result + readMethodic(MethodType, _.toTermName) + case TYPELAMBDAtype => + readMethodic(HKTypeLambda, _.toTypeName) case PARAMtype => readTypeRef() match { - case binder: PolyType => PolyParam(binder, readNat()) - case binder: MethodType => MethodParam(binder, readNat()) + case binder: LambdaType => binder.newParamRef(readNat()) } case CLASSconst => ConstantType(Constant(readType())) @@ -412,7 +407,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle } def isAbstractType(ttag: Int)(implicit ctx: Context): Boolean = nextUnsharedTag match { - case POLYtpt => + case LAMBDAtpt => val rdr = fork rdr.reader.readByte() // tag rdr.reader.readNat() // length @@ -1035,11 +1030,11 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle OrTypeTree(readTpt(), readTpt()) case ANNOTATEDtpt => Annotated(readTpt(), readTerm()) - case POLYtpt => + case LAMBDAtpt => val localCtx = localNonClassCtx val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) val body = readTpt()(localCtx) - PolyTypeTree(tparams, body) + LambdaTypeTree(tparams, body) case TYPEBOUNDStpt => TypeBoundsTree(readTpt(), readTpt()) case _ => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 688a2d007..cf99bb022 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -42,13 +42,15 @@ object Scala2Unpickler { /** Convert temp poly type to poly type and leave other types alone. */ def translateTempPoly(tp: Type)(implicit ctx: Context): Type = tp match { - case TempPolyType(tparams, restpe) => restpe.LambdaAbstract(tparams) + case TempPolyType(tparams, restpe) => + (if (tparams.head.owner.isTerm) PolyType else HKTypeLambda) + .fromParams(tparams, restpe) case tp => tp } def addConstructorTypeParams(denot: SymDenotation)(implicit ctx: Context) = { assert(denot.isConstructor) - denot.info = denot.info.LambdaAbstract(denot.owner.typeParams) + denot.info = PolyType.fromParams(denot.owner.typeParams, denot.info) } /** Convert array parameters denoting a repeated parameter of a Java method @@ -56,7 +58,7 @@ object Scala2Unpickler { */ def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match { case tp: MethodType => - val lastArg = tp.paramTypes.last + val lastArg = tp.paramInfos.last assert(lastArg isRef defn.ArrayClass) val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) val elemtp = elemtp0 match { @@ -65,12 +67,12 @@ object Scala2Unpickler { case _ => elemtp0 } - tp.derivedMethodType( + tp.derivedLambdaType( tp.paramNames, - tp.paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp), + tp.paramInfos.init :+ defn.RepeatedParamType.appliedTo(elemtp), tp.resultType) case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, arrayToRepeated(tp.resultType)) + tp.derivedLambdaType(tp.paramNames, tp.paramInfos, arrayToRepeated(tp.resultType)) } def ensureConstructor(cls: ClassSymbol, scope: Scope)(implicit ctx: Context) = @@ -745,7 +747,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas TempClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) case METHODtpe | IMPLICITMETHODtpe => val restpe = readTypeRef() - val params = until(end, readSymbolRef) + val params = until(end, readSymbolRef).asInstanceOf[List[TermSymbol]] def isImplicit = tag == IMPLICITMETHODtpe || params.nonEmpty && (params.head is Implicit) |