From af43d325b778973ad9e144b5c27c455febb98890 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 29 Jun 2016 19:05:20 +0200 Subject: Abstract type parameters out from type symbols In the new hk scheme, a type parameter can be represented by a refinement without a corresponding symbol. Therefore, we need to disentangle the info inherent in a type parameter from the contents of a type symbol. We achieve this by creating a common super trait "MemerInfo" of Symbol and RefinedType. --- src/dotty/tools/dotc/core/MemberBinding.scala | 34 +++++++++++++++++ src/dotty/tools/dotc/core/Symbols.scala | 9 ++++- src/dotty/tools/dotc/core/TypeApplications.scala | 32 +++++++++------- src/dotty/tools/dotc/core/TypeComparer.scala | 43 +++++++++++++--------- src/dotty/tools/dotc/core/Types.scala | 19 ++++++++-- .../dotc/core/classfile/ClassfileParser.scala | 2 +- src/dotty/tools/dotc/typer/Implicits.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- src/dotty/tools/dotc/typer/Typer.scala | 6 +-- 10 files changed, 110 insertions(+), 43 deletions(-) create mode 100644 src/dotty/tools/dotc/core/MemberBinding.scala (limited to 'src/dotty') diff --git a/src/dotty/tools/dotc/core/MemberBinding.scala b/src/dotty/tools/dotc/core/MemberBinding.scala new file mode 100644 index 000000000..6f081c542 --- /dev/null +++ b/src/dotty/tools/dotc/core/MemberBinding.scala @@ -0,0 +1,34 @@ +package dotty.tools.dotc.core + +import Names.Name +import Contexts.Context +import Types.{Type, TypeBounds} + +/** A common super trait of Symbol and Refinement. + * Used to capture the attributes of type parameters + * which can be implemented as either symbols or refinements. + */ +trait MemberBinding { + + /** Does this binding represent a type parameter? + * Only in that case the rest of the binding's methods are significant. + */ + def isTypeParam(implicit ctx: Context): Boolean + + /** The name of the member */ + def memberName(implicit ctx: Context): Name + + /** The info of the member */ + def memberBounds(implicit ctx: Context): TypeBounds + + /** The info of the member as seen from a prefix type. + * This can be different from `memberInfo` if the binding + * is a type symbol of a class. + */ + def memberBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds + + /** The variance of the type parameter + * @pre: isTypeParam = true + */ + def memberVariance(implicit ctx: Context): Int +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 1b605e24f..c7eb54812 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -367,7 +367,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 printing.Showable { + class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with MemberBinding with printing.Showable { type ThisName <: Name @@ -489,6 +489,13 @@ object Symbols { */ def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition + // MemberBinding methods + def isTypeParam(implicit ctx: Context) = denot.is(TypeParam) + def memberName(implicit ctx: Context): Name = name + def memberBounds(implicit ctx: Context) = denot.info.bounds + def memberBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this).bounds + def memberVariance(implicit ctx: Context) = denot.variance + // -------- Printing -------------------------------------------------------- /** The prefix string to be used when displaying this symbol without denotation */ diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index bd115fefb..d9521b3c8 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -75,10 +75,10 @@ object TypeApplications { /** Does the variance of `sym1` conform to the variance of `sym2`? * This is the case if the variances are the same or `sym` is nonvariant. */ - def varianceConforms(sym1: TypeSymbol, sym2: TypeSymbol)(implicit ctx: Context) = - sym1.variance == sym2.variance || sym2.variance == 0 + def varianceConforms(sym1: MemberBinding, sym2: MemberBinding)(implicit ctx: Context) = + sym1.memberVariance == sym2.memberVariance || sym2.memberVariance == 0 - def variancesConform(syms1: List[TypeSymbol], syms2: List[TypeSymbol])(implicit ctx: Context) = + def variancesConform(syms1: List[MemberBinding], syms2: List[MemberBinding])(implicit ctx: Context) = syms1.corresponds(syms2)(varianceConforms) /** Extractor for @@ -143,7 +143,7 @@ object TypeApplications { object EtaExpansion { def apply(tycon: TypeRef)(implicit ctx: Context) = { assert(tycon.isEtaExpandable) - tycon.EtaExpand(tycon.typeParams) + tycon.EtaExpand(tycon.typeParamSymbols) } def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = { @@ -280,7 +280,7 @@ class TypeApplications(val self: Type) extends AnyVal { * with the bounds on its hk args. See `LambdaAbstract`, where these * types get introduced, and see `isBoundedLambda` below for the test. */ - final def typeParams(implicit ctx: Context): List[TypeSymbol] = /*>|>*/ track("typeParams") /*<|<*/ { + final def typeParams(implicit ctx: Context): List[MemberBinding] = /*>|>*/ track("typeParams") /*<|<*/ { self match { case self: ClassInfo => self.cls.typeParams @@ -309,7 +309,7 @@ class TypeApplications(val self: Type) extends AnyVal { val sym = self.parent.classSymbol if (sym.isLambdaTrait) return sym.typeParams } - self.parent.typeParams.filterNot(_.name == self.refinedName) + self.parent.typeParams.filterNot(_.memberName == self.refinedName) case self: SingletonType => Nil case self: TypeProxy => @@ -319,6 +319,12 @@ class TypeApplications(val self: Type) extends AnyVal { } } + final def typeParamSymbols(implicit ctx: Context): List[TypeSymbol] = { + val tparams = typeParams + assert(tparams.isEmpty || tparams.head.isInstanceOf[Symbol]) + tparams.asInstanceOf[List[TypeSymbol]] + } + /** The named type parameters declared or inherited by this type. * These are all uninstantiated named type parameters of this type or one * of its base types. @@ -498,7 +504,7 @@ class TypeApplications(val self: Type) extends AnyVal { * v1 is compatible with v2, if v1 = v2 or v2 is non-variant. */ def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { - val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParams + val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } @@ -508,7 +514,7 @@ class TypeApplications(val self: Type) extends AnyVal { case self: RefinedType => self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo) case _ => - self.EtaExpand(self.typeParams) + self.EtaExpand(self.typeParamSymbols) } /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */ @@ -621,12 +627,12 @@ class TypeApplications(val self: Type) extends AnyVal { * @param args = `U1, ..., Un` * @param tparams are assumed to be the type parameters of `T`. */ - final def appliedTo(args: List[Type], typParams: List[TypeSymbol])(implicit ctx: Context): Type = { - def matchParams(t: Type, tparams: List[TypeSymbol], args: List[Type])(implicit ctx: Context): Type = args match { + final def appliedTo(args: List[Type], typParams: List[MemberBinding])(implicit ctx: Context): Type = { + def matchParams(t: Type, tparams: List[MemberBinding], args: List[Type])(implicit ctx: Context): Type = args match { case arg :: args1 => try { val tparam :: tparams1 = tparams - matchParams(RefinedType(t, tparam.name, arg.toBounds(tparam)), tparams1, args1) + matchParams(RefinedType(t, tparam.memberName, arg.toBounds(tparam)), tparams1, args1) } catch { case ex: MatchError => println(s"applied type mismatch: $self $args, typeParams = $typParams") // !!! DEBUG @@ -667,11 +673,11 @@ 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: Symbol)(implicit ctx: Context): TypeBounds = self match { + final def toBounds(tparam: MemberBinding)(implicit ctx: Context): TypeBounds = self match { case self: TypeBounds => // this can happen for wildcard args self case _ => - val v = tparam.variance + val v = tparam.memberVariance /* Not neeeded. if (v > 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.upper(self) else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 9909c9e8a..58c6bea3a 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -534,7 +534,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * - the type parameters of `B` match one-by-one the variances of `tparams`, * - `B` satisfies predicate `p`. */ - private def testLifted(tp1: Type, tp2: Type, tparams: List[TypeSymbol], p: Type => Boolean): Boolean = { + private def testLifted(tp1: Type, tp2: Type, tparams: List[MemberBinding], p: Type => Boolean): Boolean = { val classBounds = tp2.member(tpnme.hkApply).info.classSymbols def recur(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => @@ -647,7 +647,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } - /** Replace any top-level recursive type `{ z => T }` in `tp` with + /** Replace any top-level recursive type `{ z => T }` in `tp` with * `[z := anchor]T`. */ private def fixRecs(anchor: SingletonType, tp: Type): Type = { @@ -726,24 +726,31 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val rebindNeeded = tp2.refinementRefersToThis val base = if (rebindNeeded) ensureStableSingleton(tp1) else tp1 val rinfo2 = if (rebindNeeded) tp2.refinedInfo.substRefinedThis(tp2, base) else tp2.refinedInfo + val mbr = base.member(name) + def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2) - def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance + + def memberMatches: Boolean = mbr match { // inlined hasAltWith for performance case mbr: SingleDenotation => qualifies(mbr) case _ => mbr hasAltWith qualifies } - /*>|>*/ ctx.traceIndented(i"hasMatchingMember($base . $name :? ${tp2.refinedInfo}) ${base.member(name).info.show} $rinfo2", subtyping) /*<|<*/ { - memberMatches(base member name) || - tp1.isInstanceOf[SingletonType] && - { // special case for situations like: - // class C { type T } - // val foo: C - // foo.type <: C { type T = foo.T } - rinfo2 match { - case rinfo2: TypeAlias => - !defn.isBottomType(base.widen) && (base select name) =:= rinfo2.alias - case _ => false - } - } + + // special case for situations like: + // class C { type T } + // val foo: C + // foo.type <: C { type T = foo.T } + def selfReferentialMatch = tp1.isInstanceOf[SingletonType] && { + rinfo2 match { + case rinfo2: TypeAlias => + !defn.isBottomType(base.widen) && (base select name) =:= rinfo2.alias + case _ => false + } + } + + def varianceMatches = true // TODO: fill in + + /*>|>*/ ctx.traceIndented(i"hasMatchingMember($base . $name :? ${tp2.refinedInfo}) ${mbr.info.show} $rinfo2", subtyping) /*<|<*/ { + (memberMatches || selfReferentialMatch) && varianceMatches } } @@ -1117,8 +1124,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * in where we allow which interpretation. */ private def liftIfHK(tp1: Type, tp2: Type, op: (Type, Type) => Type) = { - val tparams1 = tp1.typeParams - val tparams2 = tp2.typeParams + val tparams1 = tp1.typeParamSymbols // TODO revise for new hk scheme + val tparams2 = tp2.typeParamSymbols def onlyNamed(tparams: List[TypeSymbol]) = tparams.forall(!_.is(ExpandedName)) if (tparams1.isEmpty || tparams2.isEmpty || onlyNamed(tparams1) && onlyNamed(tparams2)) op(tp1, tp2) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 5252a9149..cba13ef81 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -869,7 +869,7 @@ object Types { def isParamName = tp.classSymbol.typeParams.exists(_.name == tp.refinedName) if (refinementOK || isParamName) tp.underlying.underlyingClassRef(refinementOK) else NoType - case tp: RecType if refinementOK => tp.parent + case tp: RecType if refinementOK => tp.parent case _ => NoType } @@ -2051,7 +2051,7 @@ object Types { * given the refined type itself. */ abstract case class RefinedType(private var myParent: Type, refinedName: Name, private var myRefinedInfo: Type) - extends RefinedOrRecType with BindingType { + extends RefinedOrRecType with BindingType with MemberBinding { final def parent = myParent final def refinedInfo = myRefinedInfo @@ -2090,6 +2090,16 @@ object Types { if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo) else parent + // MemberBinding methods + def isTypeParam(implicit ctx: Context) = refinedInfo match { + case tp: TypeBounds => tp.isBinding + case _ => false + } + def memberName(implicit ctx: Context) = refinedName + def memberBounds(implicit ctx: Context) = refinedInfo.bounds + def memberBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = memberBounds + def memberVariance(implicit ctx: Context) = BindingKind.toVariance(refinedInfo.bounds.bindingKind) + override def equals(that: Any) = that match { case that: RefinedType => this.parent == that.parent && @@ -3120,7 +3130,10 @@ object Types { object BindingKind { def fromVariance(v: Int): BindingKind = new BindingKind((v + NonvariantBinding.n).toByte) - def toVariance(bk: BindingKind): Int = bk.n + def toVariance(bk: BindingKind): Int = { + assert(bk.n != 0) + bk.n - NonvariantBinding.n + } } // ----- Annotated and Import types ----------------------------------------------- diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index f7a69aa53..2d7b037b1 100644 --- a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -272,7 +272,7 @@ class ClassfileParser( if (sig(index) == '<') { accept('<') var tp1: Type = tp - var formals = tp.typeParams + var formals = tp.typeParamSymbols while (sig(index) != '>') { sig(index) match { case variance @ ('+' | '-' | '*') => diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 7de40294d..e21a08fb8 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -325,7 +325,7 @@ trait ImplicitRunInfo { self: RunInfo => } def addParentScope(parent: TypeRef): Unit = { iscopeRefs(parent) foreach addRef - for (param <- parent.typeParams) + for (param <- parent.typeParamSymbols) comps ++= iscopeRefs(tp.member(param.name).info) } val companion = cls.companionModule diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index a8f3b8918..8437b651c 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -989,7 +989,7 @@ class Namer { typer: Typer => if (args.nonEmpty) { val tycon = tp.withoutArgs(args) val tycon1 = this(tycon) - val tparams = tycon.typeParams + val tparams = tycon.typeParamSymbols val args1 = if (args.length == tparams.length) etaExpandIfHK(tparams, args) else args if ((tycon1 eq tycon) && (args1 eq args)) tp else tycon1.appliedTo(args1) } else mapOver(tp) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 47c3631b8..b7e2fd832 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -409,11 +409,11 @@ trait TypeAssigner { def refineNamed(tycon: Type, arg: Tree) = arg match { case ast.Trees.NamedArg(name, argtpt) => // Dotty deviation: importing ast.Trees._ and matching on NamedArg gives a cyclic ref error - val tparam = tparams.find(_.name == name) match { + val tparam = tparams.find(_.memberName == name) match { case Some(tparam) => tparam case none => ntparams.find(_.name == name).getOrElse(NoSymbol) } - if (tparam.exists) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) + if (tparam.isTypeParam) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) else errorType(i"$tycon does not have a parameter or abstract type member named $name", arg.pos) case _ => errorType(s"named and positional type arguments may not be mixed", arg.pos) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index fb3418563..225516503 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -943,14 +943,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos) args = args.take(tparams.length) } - def typedArg(arg: untpd.Tree, tparam: Symbol) = { + def typedArg(arg: untpd.Tree, tparam: MemberBinding) = { val (desugaredArg, argPt) = if (ctx.mode is Mode.Pattern) - (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.info) + (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.memberBounds) else (arg, WildcardType) val arg1 = typed(desugaredArg, argPt) - adaptTypeArg(arg1, tparam.info) + adaptTypeArg(arg1, tparam.memberBounds) } args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] } -- cgit v1.2.3