From cf794033ae42df0251fe2bc60051cb7dafb38023 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 30 Nov 2015 20:11:06 +0100 Subject: Drop requirement that an isRef type cannot be refined. It seems to complciate things with no real purpose. --- src/dotty/tools/dotc/core/Types.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 2558dcbb7..61d9093bc 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -100,7 +100,7 @@ object Types { case _ => false } - /** Is this type a (possibly aliased and/or partially applied) type reference + /** Is this type a (possibly refined or applied or aliased) type reference * to the given type symbol? * @sym The symbol to compare to. It must be a class symbol or abstract type. * It makes no sense for it to be an alias type because isRef would always @@ -113,8 +113,7 @@ object Types { case _ => this1.symbol eq sym } case this1: RefinedType => - // make sure all refinements are type arguments - this1.parent.isRef(sym) && this.argInfos.nonEmpty + this1.parent.isRef(sym) case _ => false } -- cgit v1.2.3 From f89b5628a66442edcc8c31193a559c6b5c32e837 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 2 Dec 2015 10:58:44 +0100 Subject: Add missing type params in test --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- src/dotty/tools/dotc/core/TypeApplications.scala | 8 ++++---- src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 12 ++++++------ src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 ++-- tests/pos/desugar.scala | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index d7fa183c9..cc201b66b 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -446,7 +446,7 @@ object SymDenotations { /** is this symbol a trait representing a type lambda? */ final def isLambdaTrait(implicit ctx: Context): Boolean = - isClass && name.startsWith(tpnme.LambdaPrefix) && owner == defn.ScalaPackageClass + isClass && name.startsWith(tpnme.hkLambdaPrefix) && owner == defn.ScalaPackageClass /** Is this symbol a package object or its module class? */ def isPackageObject(implicit ctx: Context): Boolean = { diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index c2738f4be..8417620f1 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -143,7 +143,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** Is receiver type higher-kinded (i.e. of kind != "*")? */ def isHK(implicit ctx: Context): Boolean = self.dealias match { case self: TypeRef => self.info.isHK - case RefinedType(_, name) => name == tpnme.hkApply || name.isLambdaArgName + case RefinedType(_, name) => name == tpnme.hkApply || name.isHkArgName case TypeBounds(_, hi) => hi.isHK case _ => false } @@ -465,7 +465,7 @@ class TypeApplications(val self: Type) extends AnyVal { val lambda = defn.LambdaTrait(boundSyms.map(_.variance)) def toHK(tp: Type) = (rt: RefinedType) => { val argRefs = boundSyms.indices.toList.map(i => - RefinedThis(rt).select(tpnme.LambdaArgName(i))) + RefinedThis(rt).select(tpnme.hkArg(i))) val substituted = if (cycleParanoid) new ctx.SafeSubstMap(boundSyms, argRefs).apply(tp) else tp.subst(boundSyms, argRefs) @@ -576,8 +576,8 @@ class TypeApplications(val self: Type) extends AnyVal { new TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(RefinedThis(binder), name) if binder eq self => - assert(name.isLambdaArgName) - RefinedThis(reduced).select(reduced.typeParams.apply(name.LambdaArgIndex)) + assert(name.isHkArgName) + RefinedThis(reduced).select(reduced.typeParams.apply(name.hkArgIndex)) case _ => mapOver(tp) } diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 1d9928e2d..6b37227b2 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -669,7 +669,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private def needsEtaLift(tp: Type, target: RefinedType): Boolean = { // if (tp.isLambda != tp.isHK) println(i"discrepancy for $tp, isLambda = ${tp.isLambda}, isHK = ${tp.isHK}") val name = target.refinedName - (name.isLambdaArgName || (name eq tpnme.hkApply)) && + (name.isHkArgName || (name eq tpnme.hkApply)) && tp.exists && !tp.isLambda // we do encounter Lambda classes without any arguments here } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 61d9093bc..566268455 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -855,7 +855,7 @@ object Types { object instantiate extends TypeMap { var isSafe = true def apply(tp: Type): Type = tp match { - case TypeRef(RefinedThis(`pre`), name) if name.isLambdaArgName => + case TypeRef(RefinedThis(`pre`), name) if name.isHkArgName => member(name).info match { case TypeAlias(alias) => alias case _ => isSafe = false; tp @@ -868,7 +868,7 @@ object Types { } } def instArg(tp: Type): Type = tp match { - case tp @ TypeAlias(TypeRef(RefinedThis(`pre`), name)) if name.isLambdaArgName => + case tp @ TypeAlias(TypeRef(RefinedThis(`pre`), name)) if name.isHkArgName => member(name).info match { case TypeAlias(alias) => tp.derivedTypeAlias(alias) // needed to keep variance case bounds => bounds @@ -1899,15 +1899,15 @@ object Types { private def checkInst(implicit ctx: Context): this.type = { if (Config.checkLambdaVariance) refinedInfo match { - case refinedInfo: TypeBounds if refinedInfo.variance != 0 && refinedName.isLambdaArgName => + case refinedInfo: TypeBounds if refinedInfo.variance != 0 && refinedName.isHkArgName => val cls = parent.LambdaClass(forcing = false) if (cls.exists) - assert(refinedInfo.variance == cls.typeParams.apply(refinedName.LambdaArgIndex).variance, - s"variance mismatch for $this, $cls, ${cls.typeParams}, ${cls.typeParams.apply(refinedName.LambdaArgIndex).variance}, ${refinedInfo.variance}") + assert(refinedInfo.variance == cls.typeParams.apply(refinedName.hkArgIndex).variance, + s"variance mismatch for $this, $cls, ${cls.typeParams}, ${cls.typeParams.apply(refinedName.hkArgIndex).variance}, ${refinedInfo.variance}") case _ => } if (Config.checkProjections && - (refinedName == tpnme.hkApply || refinedName.isLambdaArgName) && + (refinedName == tpnme.hkApply || refinedName.isHkArgName) && parent.noHK) assert(false, s"illegal refinement of first-order type: $this") this diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 06fe0c9ef..04c9252eb 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -190,14 +190,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { // LambdaI{...}.HK$i val simplifyArgs = new TypeMap { override def apply(tp: Type) = tp match { - case tp @ TypeRef(RefinedThis(_), name) if name.isLambdaArgName => + case tp @ TypeRef(RefinedThis(_), name) if name.isHkArgName => TypeRef(NoPrefix, tp.symbol.asType) case _ => mapOver(tp) } } (parent, simplifyArgs(tp.refinedInfo.followTypeAlias), Nil) - } else if (name.isLambdaArgName) { + } else if (name.isHkArgName) { val (prefix, body, argBindings) = decomposeHKApply(parent) (prefix, body, (name, tp.refinedInfo) :: argBindings) } else (tp, NoType, Nil) diff --git a/tests/pos/desugar.scala b/tests/pos/desugar.scala index 0d3b6d8ca..cc6794985 100644 --- a/tests/pos/desugar.scala +++ b/tests/pos/desugar.scala @@ -72,7 +72,7 @@ object desugar { object misc { 'hello s"this is a $x + ${x + y} string" - type ~ = Tuple2 + type ~[X, Y] = Tuple2[X, Y] val pair: Int ~ String = 1 -> "abc" def foo(xs: Int*) = xs.length foo(list: _*) -- cgit v1.2.3 From 67d28339d91f912c2894a05110d713f077458feb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 2 Dec 2015 14:13:15 +0100 Subject: Drop redundant check in beta reduce --- src/dotty/tools/dotc/core/Types.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 566268455..84d74b0f4 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -883,12 +883,10 @@ object Types { instantiate(tp) } /** Reduce rhs of $hkApply to make it stand alone */ - def betaReduce(tp: Type) = - if (pre.parent.isSafeLambda) { - val reduced = instTop(tp) - if (instantiate.isSafe) reduced else NoType - } - else NoType + def betaReduce(tp: Type) = { + val reduced = instTop(tp) + if (instantiate.isSafe) reduced else NoType + } pre.refinedInfo match { case TypeAlias(alias) => if (pre.refinedName ne name) loop(pre.parent) -- cgit v1.2.3 From 8db7a9b8914e0f66207db9d998f1186f8e195f85 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 3 Dec 2015 10:31:15 +0100 Subject: Fix bug computing typeParams for hk types Arg bounds do not count is bindings. Also: TypeLambda's $Apply binding should be covariant, because the parameter is (not sure it matters though). --- src/dotty/tools/dotc/core/TypeApplications.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 5b34fe662..5f7b730d2 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -57,7 +57,7 @@ object TypeApplications { assert(argBoundss.length == variances.length) RefinedType( argRefinements(defn.LambdaTrait(variances).typeRef, 0, argBoundss), - tpnme.hkApply, rt => bodyFn(rt).bounds) + tpnme.hkApply, rt => TypeAlias(bodyFn(rt), 1)) } def unapply(tp: Type)(implicit ctx: Context): Option[(List[Int], List[TypeBounds], Type)] = tp match { diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 84d74b0f4..4af4d0c14 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1929,7 +1929,7 @@ object Types { false } override def computeHash = doHash(refinedName, refinedInfo, parent) - override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)" // !!! TODO: remove + override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)" } class CachedRefinedType(parent: Type, refinedName: Name, infoFn: RefinedType => Type) extends RefinedType(parent, refinedName) { -- cgit v1.2.3 From f8d82c2634dcf9d94037b107d49d088e89f7070f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 4 Dec 2015 17:55:21 +0100 Subject: Switch to new hk scheme. --- src/dotty/tools/dotc/core/ConstraintHandling.scala | 10 + src/dotty/tools/dotc/core/TypeApplications.scala | 303 ++++++++++----------- src/dotty/tools/dotc/core/TypeComparer.scala | 146 +++++++--- src/dotty/tools/dotc/core/Types.scala | 36 +-- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 42 ++- .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 7 +- src/dotty/tools/dotc/typer/Applications.scala | 4 +- src/dotty/tools/dotc/typer/Typer.scala | 7 +- tests/pos/t2693.scala | 6 +- 9 files changed, 310 insertions(+), 251 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala index 577b958c8..ff7afe99a 100644 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -232,6 +232,16 @@ trait ConstraintHandling { } } + /** Instantiate `param` to `tp` if the constraint stays satisfiable */ + protected def tryInstantiate(param: PolyParam, tp: Type): Boolean = { + val saved = constraint + constraint = + if (addConstraint(param, tp, fromBelow = true) && + addConstraint(param, tp, fromBelow = false)) constraint.replace(param, tp) + else saved + constraint ne saved + } + /** Check that constraint is fully propagated. See comment in Config.checkConstraintsPropagated */ def checkPropagated(msg: => String)(result: Boolean): Boolean = { if (Config.checkConstraintsPropagated && result && addConstraintInvocations == 0) { diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 4c9746c39..0f9cb8230 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -37,6 +37,15 @@ object TypeApplications { case _ => tp } + /** 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 variancesConform(syms1: List[TypeSymbol], syms2: List[TypeSymbol])(implicit ctx: Context) = + syms1.corresponds(syms2)(varianceConforms) + /** Extractor for * * [v1 X1: B1, ..., vn Xn: Bn] -> T @@ -123,25 +132,7 @@ object TypeApplications { * T { ... } # $Apply */ object AppliedType { - def apply(tp: Type, args: List[Type])(implicit ctx: Context): Type = { - def matchParams(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match { - case arg :: args1 => - try { - val tparam :: tparams1 = tparams - matchParams(RefinedType(tp, tparam.name, arg.toBounds(tparam)), tparams1, args1) - } catch { - case ex: MatchError => - println(s"applied type mismatch: $tp $args, typeParams = ${tp.classSymbol.typeParams}") // !!! DEBUG - println(s"precomplete decls = ${tp.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") - throw ex - } - case nil => tp - } - assert(args.nonEmpty) - val cls = tp.classSymbol - val refined = matchParams(tp, cls.typeParams, args) - if (cls.isLambdaTrait) TypeRef(refined, tpnme.hkApply) else refined - } + def apply(tp: Type, args: List[Type])(implicit ctx: Context): Type = tp.appliedTo(args) def unapply(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { case TypeRef(prefix, tpnme.hkApply) => unapp(prefix) @@ -204,18 +195,22 @@ class TypeApplications(val self: Type) extends AnyVal { case self: ClassInfo => self.cls.typeParams case self: TypeRef => - val tsym = self.typeSymbol + val tsym = self.symbol if (tsym.isClass) tsym.typeParams else if (tsym.isAliasType) self.underlying.typeParams - else { - val lam = LambdaClass(forcing = false) - if (lam.exists) lam.typeParams else Nil - } + else if (tsym.isCompleting) + // We are facing a problem when computing the type parameters of an uncompleted + // abstract type. We can't access the bounds of the symbol yet because that + // would cause a cause a cyclic reference. So we return `Nil` instead + // and try to make up for it later. The acrobatics in Scala2Unpicker#readType + // for reading a TypeRef show what's neeed. + Nil + else tsym.info.typeParams case self: RefinedType => val hkParams = self.hkTypeParams if (hkParams.nonEmpty) hkParams else self.parent.typeParams.filterNot(_.name == self.refinedName) - case self: SingletonType => + case self: SingletonType => Nil case self: TypeProxy => self.underlying.typeParams @@ -224,20 +219,15 @@ class TypeApplications(val self: Type) extends AnyVal { } } - /** The type parameters of the underlying class. - * This is like `typeParams`, except for 3 differences. - * First, it does not adjust type parameters in refined types. I.e. type arguments - * do not remove corresponding type parameters. - * Second, it will return Nil for BoundTypes because we might get a NullPointer exception - * on PolyParam#underlying otherwise (demonstrated by showClass test). - * Third, it won't return abstract higher-kinded type parameters, i.e. the type parameters of - * an abstract type are always empty. + /** The higherkinded type parameters in case this is a type lambda + * + * [X1, ..., Xn] -> T + * + * These are the parameters of the underlying lambda class. + * Returns `Nil` for all other types. */ - final def hkTypeParams(implicit ctx: Context): List[TypeSymbol] = self match { - case TypeLambda(_, _, body) => self.typeSymbol.typeParams.head :: body.hkTypeParams - case TypeBounds(lo, hi) => hi.hkTypeParams - case _ => Nil - } + final def hkTypeParams(implicit ctx: Context): List[TypeSymbol] = + self.LambdaTrait.typeParams final def paramBounds(implicit ctx: Context): List[TypeBounds] = typeParams.map(self.memberInfo(_).bounds) @@ -251,11 +241,41 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => NoSymbol } + /** A type ref is eta expandable if it refers to a non-lambda class. + * In that case we can look for parameterized base types fo the type + * to eta expand them. + */ def isEtaExpandable(implicit ctx: Context) = self match { case self: TypeRef => self.symbol.isClass && !self.name.isLambdaTraitName case _ => false } + /** 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) + */ + def LambdaAbstract(tparams: List[Symbol])(implicit ctx: Context): Type = { + def expand(tp: Type) = { + TypeLambda(tparams.map(_.variance), tparams.map(_.info.bounds), + rt => new ctx.SafeSubstMap(tparams, argRefs(rt, tparams.length)).apply(tp)) + } + self match { + case self: TypeAlias => + self.derivedTypeAlias(expand(self.alias)) + case self @ TypeBounds(lo, hi) => + self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi))) + case _ => expand(self) + } + } +/* + def betaReduce(implicit ctx: Context): Type = self.stripTypeVar match { + case TypeRef(prefix, tpnme.hkApply) => + prefix.betaReduce + case self @ RefinedType(parent, tpnme.hkArg) if parent.isTypeLambda => + HKApplication(parent, self.refinedInfo.dropAlias) + } +*/ /** Adapt argument A to type parameter P in the case P is higher-kinded. * This means: * (1) Make sure that A is a type lambda, if necessary by eta-expanding it. @@ -305,6 +325,7 @@ class TypeApplications(val self: Type) extends AnyVal { } } + /* /** If type `self` is equal, aliased-to, or upperbounded-by a type of the form * `LambdaXYZ { ... }`, the class symbol of that type, otherwise NoSymbol. * symbol of that type, otherwise NoSymbol. @@ -338,11 +359,11 @@ class TypeApplications(val self: Type) extends AnyVal { /** Is type `self` a Lambda with all hk$i fields fully instantiated? */ def isInstantiatedLambda(implicit ctx: Context): Boolean = isSafeLambda && typeParams.isEmpty - +*/ /** Is receiver type higher-kinded (i.e. of kind != "*")? */ def isHK(implicit ctx: Context): Boolean = self.dealias match { case self: TypeRef => self.info.isHK - case RefinedType(_, name) => name == tpnme.hkApply || name.isHkArgName + case RefinedType(_, name) => name == tpnme.hkApply case TypeBounds(_, hi) => hi.isHK case _ => false } @@ -361,14 +382,14 @@ class TypeApplications(val self: Type) extends AnyVal { * * but without forcing anything. */ - def noHK(implicit ctx: Context): Boolean = self.stripTypeVar match { + def classNotLambda(implicit ctx: Context): Boolean = self.stripTypeVar match { case self: RefinedType => - self.parent.noHK + self.parent.classNotLambda case self: TypeRef => - (self.denot.exists) && { + self.denot.exists && { val sym = self.symbol if (sym.isClass) !sym.isLambdaTrait - else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.noHK + else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.classNotLambda } case _ => false @@ -376,72 +397,66 @@ class TypeApplications(val self: Type) extends AnyVal { /** Encode the type resulting from applying this type to given arguments */ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { - def matchParams(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match { - case arg :: args1 => - if (tparams.isEmpty) { - println(s"applied type mismatch: $self $args, typeParams = $typeParams, tsym = ${self.typeSymbol.debugString}") // !!! DEBUG - println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") - } - val tparam = tparams.head - val tp1 = RefinedType(tp, tparam.name, arg.toBounds(tparam)) - matchParams(tp1, tparams.tail, args1) - case nil => tp + def substHkArgs = new TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(RefinedThis(rt), name) if rt.eq(self) && name.isHkArgName => + args(name.hkArgIndex) + case _ => + mapOver(tp) + } + } + if (args.isEmpty || ctx.erasedTypes) self + else self.stripTypeVar match { + case EtaExpansion(self1) => + self1.appliedTo(args) + case TypeLambda(_, _, body) if !args.exists(_.isInstanceOf[TypeBounds]) => + substHkArgs(body) + case self: PolyType => + self.instantiate(args) + case _ => + appliedTo(args, typeParams) } + } - /** Instantiate type `tp` with `args`. - * @param original The original type for which we compute the type parameters - * This makes a difference for refinement types, because - * refinements bind type parameters and thereby remove them - * from `typeParams`. - */ - def instantiate(tp: Type, original: Type): Type = tp match { - case tp: TypeRef => - val tsym = tp.symbol - if (tsym.isAliasType) tp.underlying.appliedTo(args) - else { - val safeTypeParams = - if (tsym.isClass || !tp.typeSymbol.isCompleting) original.typeParams - else { - ctx.warning(i"encountered F-bounded higher-kinded type parameters for $tsym; assuming they are invariant") - defn.LambdaTrait(args map alwaysZero).typeParams // @@@ can we force? - } - matchParams(tp, safeTypeParams, args) + 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 { + case arg :: args1 => + try { + val tparam :: tparams1 = tparams + matchParams(RefinedType(t, tparam.name, arg.toBounds(tparam)), tparams1, args1) + } catch { + case ex: MatchError => + println(s"applied type mismatch: $self $args, typeParams = $typParams") // !!! DEBUG + //println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") + throw ex } - case tp: RefinedType => - val redux = tp.EtaReduce - if (redux.exists) redux.appliedTo(args) // Rewrite ([hk$0] => C[hk$0])(T) to C[T] - else tp.derivedRefinedType( - instantiate(tp.parent, original), - tp.refinedName, - tp.refinedInfo) - case tp: TypeProxy => - instantiate(tp.underlying, original) - case tp: PolyType => - tp.instantiate(args) - case ErrorType => - tp + case nil => t } - - /** Same as isHK, except we classify all abstract types as HK, - * (they must be, because they are applied). This avoids some forcing and - * CyclicReference errors of the standard isHK. - */ - def isKnownHK(tp: Type): Boolean = tp match { - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) sym.isLambdaTrait - else !sym.isAliasType || isKnownHK(tp.info) - case tp: TypeProxy => isKnownHK(tp.underlying) - case _ => false + assert(args.nonEmpty) + matchParams(self, typParams, args) match { + case refined @ RefinedType(_, pname) if pname.isHkArgName => + TypeRef(refined, tpnme.hkApply) + case refined => + refined } + } - if (args.isEmpty || ctx.erasedTypes) self - else { - val res = instantiate(self, self) - if (isKnownHK(res)) TypeRef(res, tpnme.hkApply) else res + + final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil) + final def appliedTo(arg1: Type, arg2: Type)(implicit ctx: Context): Type = appliedTo(arg1 :: arg2 :: Nil) + + final def safeAppliedTo(args: List[Type])(implicit ctx: Context) = { + val safeTypeParams = self match { + case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting => + // This happens when unpickling e.g. scala$collection$generic$GenMapFactory$$CC + ctx.warning(i"encountered F-bounded higher-kinded type parameters for ${self.symbol}; assuming they are invariant") + defn.LambdaTrait(args map alwaysZero).typeParams + case _ => + typeParams } + appliedTo(args, safeTypeParams) } - +/* /** Simplify a fully instantiated type of the form `LambdaX{... type Apply = T } # Apply` to `T`. */ def simplifyApply(implicit ctx: Context): Type = self match { @@ -452,9 +467,7 @@ class TypeApplications(val self: Type) extends AnyVal { } case _ => self } - - final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil) - final def appliedTo(arg1: Type, arg2: Type)(implicit ctx: Context): Type = appliedTo(arg1 :: arg2 :: Nil) +*/ /** Turn this type, which is used as an argument for * type parameter `tparam`, into a TypeBounds RHS @@ -558,26 +571,9 @@ class TypeApplications(val self: Type) extends AnyVal { * otherwise return Nil. * Existential types in arguments are returned as TypeBounds instances. */ - final def argInfos(implicit ctx: Context): List[Type] = { - var tparams: List[TypeSymbol] = null - def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp.stripTypeVar match { - case tp @ RefinedType(tycon, name) => - val buf = recur(tycon, refineCount + 1) - if (buf == null) null - else { - if (tparams == null) tparams = tycon.typeParams - if (buf.size < tparams.length) { - val tparam = tparams(buf.size) - if (name == tparam.name) buf += tp.refinedInfo.argInfo - else null - } else null - } - case _ => - if (refineCount == 0) null - else new mutable.ListBuffer[Type] - } - val buf = recur(self, 0) - if (buf == null || buf.size != tparams.length) Nil else buf.toList + final def argInfos(implicit ctx: Context): List[Type] = self match { + case AppliedType(tycon, args) => args + case _ => Nil } /** Argument types where existential types in arguments are disallowed */ @@ -600,6 +596,11 @@ class TypeApplications(val self: Type) extends AnyVal { self } + final def typeConstructor(implicit ctx: Context): Type = self.stripTypeVar match { + case AppliedType(tycon, _) => tycon + case self => self + } + /** If this is the image of a type argument; recover the type argument, * otherwise NoType. */ @@ -609,6 +610,11 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => NoType } + def dropAlias(implicit ctx: Context): Type = self match { + case TypeAlias(alias) => alias + case _ => self + } + /** The element type of a sequence or array */ def elemType(implicit ctx: Context): Type = self match { case defn.ArrayOf(elemtp) => elemtp @@ -639,7 +645,7 @@ class TypeApplications(val self: Type) extends AnyVal { } recur(self) } - +/* /** The typed lambda abstraction of this type `T` relative to `boundSyms`. * This is: * @@ -690,7 +696,7 @@ class TypeApplications(val self: Type) extends AnyVal { expand(self) } } - +*/ /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` * in a context where type parameters `U1,...,Un` are expected to * @@ -701,20 +707,17 @@ class TypeApplications(val self: Type) extends AnyVal { * - `T1,...,Tn` otherwise. * v1 is compatible with v2, if v1 = v2 or v2 is non-variant. */ - def EtaExpand(tparams: List[Symbol])(implicit ctx: Context): Type = { - def varianceCompatible(actual: Symbol, formal: Symbol) = - formal.variance == 0 || actual.variance == formal.variance - val tparamsToUse = - if (typeParams.corresponds(tparams)(varianceCompatible)) tparams else typeParams + def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { + val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParams self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } - +/* /** Eta expand if `bound` is a higher-kinded type */ def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type = if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand(bound.typeParams) else self - +*/ /** Eta expand the prefix in front of any refinements. */ def EtaExpandCore(implicit ctx: Context): Type = self.stripTypeVar match { case self: RefinedType => @@ -722,7 +725,7 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => self.EtaExpand(self.typeParams) } - +/* /** If `self` is a (potentially partially instantiated) eta expansion of type T, return T, * otherwise NoType. More precisely if `self` is of the form * @@ -809,31 +812,21 @@ class TypeApplications(val self: Type) extends AnyVal { * @param classBounds A hint to bound the search. Only types that derive from one of the * classes in classBounds are considered. */ - def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol])(implicit ctx: Context): Boolean = { - def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match { + def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol] = Nil)(implicit ctx: Context): Boolean = { + def recur(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => - val tp = self.baseTypeWithArgs(bc) - val targs = tp.argInfos - val tycon = tp.withoutArgs(targs) + val baseRef = self.baseTypeRef(bc) def variancesMatch(param1: Symbol, param2: Symbol) = param2.variance == param2.variance || param2.variance == 0 - if (classBounds.exists(tycon.derivesFrom(_)) && - tycon.typeParams.corresponds(tparams)(variancesMatch)) { - val expanded = tycon.EtaExpand(tparams) - val lifted = (expanded /: targs) { (partialInst, targ) => - val tparam = partialInst.typeParams.head - RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance)) - } - ctx.traceIndented(i"eta lifting $self --> $lifted", hk) { - p(lifted) || tryLift(bcs1) - } - } - else tryLift(bcs1) + (classBounds.exists(bc.derivesFrom) && + baseRef.typeParams.corresponds(tparams)(variancesMatch) && + p(baseRef.appliedTo(self.baseArgInfos(bc))) + || + recur(bcs1)) case nil => false } - tparams.nonEmpty && - (typeParams.hasSameLengthAs(tparams) && p(EtaExpand(tparams)) || - classBounds.nonEmpty && tryLift(self.baseClasses)) + classBounds.nonEmpty && recur(self.baseClasses) } + */ } diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 6b37227b2..bdabca8df 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -12,6 +12,7 @@ import util.{Stats, DotClass, SimpleMap} import config.Config import config.Printers._ import TypeErasure.{erasedLub, erasedGlb} +import TypeApplications._ import scala.util.control.NonFatal /** Provides methods to compare types. @@ -150,20 +151,21 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case info1: TypeAlias => compareNamed(info1.alias, tp2) case _ => val sym1 = tp1.symbol - (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) - ctx.erasedTypes || - sym1.isStaticOwner || - isSubType(tp1.prefix, tp2.prefix) || - thirdTryNamed(tp1, tp2) - else - (tp1.name eq tp2.name) && - isSubType(tp1.prefix, tp2.prefix) && - (tp1.signature == tp2.signature) && - !tp1.isInstanceOf[WithFixedSym] && - !tp2.isInstanceOf[WithFixedSym] || - compareHK(tp1, tp2, inOrder = true) || - compareHK(tp2, tp1, inOrder = false) || - thirdTryNamed(tp1, tp2)) + if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) + ctx.erasedTypes || + sym1.isStaticOwner || + isSubType(tp1.prefix, tp2.prefix) || + thirdTryNamed(tp1, tp2) + else + ( (tp1.name eq tp2.name) + && isSubType(tp1.prefix, tp2.prefix) + && tp1.signature == tp2.signature + && !tp1.isInstanceOf[WithFixedSym] + && !tp2.isInstanceOf[WithFixedSym] + ) || + compareHK(tp1, tp2, inOrder = true) || + compareHK(tp2, tp1, inOrder = false) || + thirdTryNamed(tp1, tp2) } case _ => compareHK(tp2, tp1, inOrder = false) || @@ -239,9 +241,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: NamedType => tp1.info match { case info1: TypeAlias => isSubType(info1.alias, tp2) - case _ => compareHK(tp1, tp2, inOrder = true) || thirdTry(tp1, tp2) - // Note: If we change the order here, doing compareHK first and following aliases second, - // we get a -Ycheck error when compiling dotc/transform. Need to investigate. + case _ => + compareHK(tp1, tp2, inOrder = true) || + thirdTry(tp1, tp2) } case tp1: PolyParam => def flagNothingBound = { @@ -344,12 +346,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: AndType => // Delay calling `compareRefinedSlow` because looking up a member // of an `AndType` can lead to a cascade of subtyping checks + // This twist is needed to make collection/generic/ParFactory.scala compile fourthTry(tp1, tp2) || compareRefinedSlow case _ => compareRefinedSlow || fourthTry(tp1, tp2) } + def etaExpandedSubType(tp1: Type) = + isSubType(tp1.typeConstructor.EtaExpand(tp2.typeParams), tp2) normalPath || - needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2), classBounds(tp2)) + needsEtaLift(tp1, tp2) && + testLifted(tp1, tp2, tp2.typeParams, etaExpandedSubType) } else // fast path, in particular for refinements resulting from parameterization. isSubType(tp1, skipped2) && @@ -471,7 +477,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths case tp1: RefinedType => isNewSubType(tp1.parent, tp2) || - needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil) + needsEtaLift(tp2, tp1) && + tp2.typeParams.length == tp1.typeParams.length && + isSubType(tp1, tp2.EtaExpand(tp1.typeParams)) case AndType(tp11, tp12) => // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 // and analogously for T11 & (T121 | T122) & T12 <: T2 @@ -502,19 +510,89 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } - /** If `projection` is a hk projection T#$apply - * and `other` is not a hk projection, then convert `other` to a hk projection `U`, and - * continue with `T <:< U` if `inOrder` is true and `U <:< T` otherwise. + /** Does `tp` need to be eta lifted to be comparable to `target`? + * This is the case if: + * - target is a type lambda, and + * - `tp` is eta-expandable (i.e. is a non-lambda class ref) */ - def compareHK(projection: NamedType, other: Type, inOrder: Boolean) = - projection.name == tpnme.hkApply && - !other.isHKApply && - other.testLifted(projection.prefix.LambdaClass(forcing = true).typeParams, - if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix), - if (inOrder) Nil else classBounds(projection.prefix)) + private def needsEtaLift(tp: Type, target: RefinedType): Boolean = + target.refinedName == tpnme.hkApply && tp.isEtaExpandable - /** The class symbols bounding the type of the `Apply` member of `tp` */ - private def classBounds(tp: Type) = tp.member(tpnme.hkApply).info.classSymbols + /** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where + * - `B` derives from one of the class symbols of `tp2`, + * - the type parameters of `B` match one-by-one the variances of `tparams`, + * - `B` satisfies predicate `p`. + */ + private def testLifted[T](tp1: Type, tp2: Type, tparams: List[TypeSymbol], p: Type => Boolean): Boolean = { + val classBounds = tp2.member(tpnme.hkApply).info.classSymbols + def recur(bcs: List[ClassSymbol]): Boolean = bcs match { + case bc :: bcs1 => + val baseRef = tp1.baseTypeRef(bc) + (classBounds.exists(bc.derivesFrom) && + variancesConform(baseRef.typeParams, tparams) && + p(baseRef.appliedTo(tp1.baseArgInfos(bc))) + || + recur(bcs1)) + case nil => + false + } + recur(tp1.baseClasses) + } + + /** If `projection` is a hk projection T#$apply with a constrainable poly param + * as type constructor and `other` is not a hk projection, then perform the following + * steps: + * + * (1) If not `inOrder` then perform the next steps until they all succeed + * for each base type of other which + * - derives from a class bound of `projection`, + * - has the same number of type parameters than `projection` + * - has type parameter variances which conform to those of `projection`. + * If `inOrder` then perform the same steps on the original `other` type. + * + * (2) Try to eta expand the constructor of `other`. + * + * (3a) In mode `TypeVarsMissConetxt` replace the projection's hk constructor parameter + * by the eta expansion of step (2) reapplied to the projection's arguments. + * (3b) In normal mode, try to unify the projection's hk constructor parameter with + * the eta expansion of step(2) + * + * (4) If `inOrder`, test `projection <: other` else test `other <: projection`. + */ + def compareHK(projection: NamedType, other: Type, inOrder: Boolean): Boolean = { + def tryInfer(tp: Type): Boolean = ctx.traceIndented(i"compareHK($projection, $other, inOrder = $inOrder, constr = $tp)", subtyping) { + tp match { + case tp: TypeVar => tryInfer(tp.underlying) + case param: PolyParam if canConstrain(param) => + + def unifyWith(liftedOther: Type): Boolean = { + subtyping.println(i"unify with $liftedOther") + liftedOther.typeConstructor.widen match { + case tycon: TypeRef if tycon.isEtaExpandable && tycon.typeParams.nonEmpty => + val (ok, projection1) = + if (ctx.mode.is(Mode.TypevarsMissContext)) + (true, EtaExpansion(tycon).appliedTo(projection.argInfos)) + else + (tryInstantiate(param, EtaExpansion(tycon)), projection) + ok && + (if (inOrder) isSubType(projection1, other) else isSubType(other, projection1)) // ### move out? + case _ => + false + } + } + val hkTypeParams = param.typeParams + subtyping.println(i"classBounds = ${projection.prefix.member(tpnme.hkApply).info.classSymbols}") + subtyping.println(i"base classes = ${other.baseClasses}") + subtyping.println(i"type params = $hkTypeParams") + if (inOrder) unifyWith(other) + else testLifted(other, projection.prefix, hkTypeParams, unifyWith) + case _ => + false + } + } + projection.name == tpnme.hkApply && !other.isHKApply && + tryInfer(projection.prefix.typeConstructor.dealias) + } /** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time * to keep the constraint as wide as possible. Specifically, if @@ -665,14 +743,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => false } - /** Does `tp` need to be eta lifted to be comparable to `target`? */ - private def needsEtaLift(tp: Type, target: RefinedType): Boolean = { - // if (tp.isLambda != tp.isHK) println(i"discrepancy for $tp, isLambda = ${tp.isLambda}, isHK = ${tp.isHK}") - val name = target.refinedName - (name.isHkArgName || (name eq tpnme.hkApply)) && - tp.exists && !tp.isLambda // we do encounter Lambda classes without any arguments here - } - /** Narrow gadt.bounds for the type parameter referenced by `tr` to include * `bound` as an upper or lower bound (which depends on `isUpper`). * Test that the resulting bounds are still satisfiable. diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 4af4d0c14..54e6397bb 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -893,7 +893,7 @@ object Types { else if (!pre.refinementRefersToThis) alias else alias match { case TypeRef(RefinedThis(`pre`), aliasName) => lookupRefined(aliasName) // (1) - case _ => if (name == tpnme.hkApply) betaReduce(alias) else NoType // (2) + case _ => if (name == tpnme.hkApply) betaReduce(alias) else NoType // (2) // ### use TypeApplication's betaReduce } case _ => loop(pre.parent) } @@ -1513,7 +1513,12 @@ object Types { else { val res = prefix.lookupRefined(name) if (res.exists) res - else if (name == tpnme.hkApply && prefix.noHK) derivedSelect(prefix.EtaExpandCore) + else if (name == tpnme.hkApply && prefix.classNotLambda) { + // After substitution we might end up with a type like + // `C { type hk$0 = T0; ...; type hk$n = Tn } # $Apply` + // where C is a class. In that case we eta expand `C`. + derivedSelect(prefix.EtaExpandCore) + } else newLikeThis(prefix) } @@ -1753,8 +1758,8 @@ object Types { object TypeRef { def checkProjection(prefix: Type, name: TypeName)(implicit ctx: Context) = - if (name == tpnme.hkApply && prefix.noHK) - assert(false, s"bad type : $prefix.$name should not be $$applied") + if (name == tpnme.hkApply && prefix.classNotLambda) + assert(false, s"bad type : $prefix.$name does not allow $$Apply projection") /** Create type ref with given prefix and name */ def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = { @@ -1894,22 +1899,7 @@ object Types { override def underlying(implicit ctx: Context) = parent - private def checkInst(implicit ctx: Context): this.type = { - if (Config.checkLambdaVariance) - refinedInfo match { - case refinedInfo: TypeBounds if refinedInfo.variance != 0 && refinedName.isHkArgName => - val cls = parent.LambdaClass(forcing = false) - if (cls.exists) - assert(refinedInfo.variance == cls.typeParams.apply(refinedName.hkArgIndex).variance, - s"variance mismatch for $this, $cls, ${cls.typeParams}, ${cls.typeParams.apply(refinedName.hkArgIndex).variance}, ${refinedInfo.variance}") - case _ => - } - if (Config.checkProjections && - (refinedName == tpnme.hkApply || refinedName.isHkArgName) && - parent.noHK) - assert(false, s"illegal refinement of first-order type: $this") - this - } + private def checkInst(implicit ctx: Context): this.type = this def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): RefinedType = if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this @@ -2743,9 +2733,9 @@ object Types { abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) { /** pre: this is a type alias */ - def derivedTypeAlias(tp: Type, variance: Int = this.variance)(implicit ctx: Context) = - if ((lo eq tp) && (variance == this.variance)) this - else TypeAlias(tp, variance) + def derivedTypeAlias(alias: Type, variance: Int = this.variance)(implicit ctx: Context) = + if ((alias eq this.alias) && (variance == this.variance)) this + else TypeAlias(alias, variance) override def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = { val v = this commonVariance that diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index c0136538b..8fccb8973 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -9,6 +9,7 @@ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annot import collection.mutable import NameOps._ import TastyBuffer._ +import TypeApplications._ class TreePickler(pickler: TastyPickler) { val buf = new TreeBuffer @@ -142,6 +143,9 @@ class TreePickler(pickler: TastyPickler) { } def pickleNewType(tpe: Type, richTypes: Boolean): Unit = try { tpe match { + case AppliedType(tycon, args) => + writeByte(APPLIEDtype) + withLength { pickleType(tycon); args.foreach(pickleType(_)) } case ConstantType(value) => pickleConstant(value) case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) => @@ -181,17 +185,16 @@ class TreePickler(pickler: TastyPickler) { pickleNameAndSig(tpe.name, tpe.signature); pickleType(tpe.prefix) } case tpe: NamedType => - if (tpe.name == tpnme.hkApply && tpe.prefix.argInfos.nonEmpty && tpe.prefix.isInstantiatedLambda) - // instantiated lambdas are pickled as APPLIEDTYPE; #Apply will - // be reconstituted when unpickling. - pickleType(tpe.prefix) - else if (isLocallyDefined(tpe.symbol)) { - writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol) - pickleSymRef(tpe.symbol); pickleType(tpe.prefix) - } - else { - writeByte(if (tpe.isType) TYPEREF else TERMREF) - pickleName(tpe.name); pickleType(tpe.prefix) + tpe match { + case _ => + if (isLocallyDefined(tpe.symbol)) { + writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol) + pickleSymRef(tpe.symbol); pickleType(tpe.prefix) + } + else { + writeByte(if (tpe.isType) TYPEREF else TERMREF) + pickleName(tpe.name); pickleType(tpe.prefix) + } } case tpe: ThisType => if (tpe.cls.is(Flags.Package) && !tpe.cls.isEffectiveRoot) @@ -211,18 +214,11 @@ class TreePickler(pickler: TastyPickler) { case tpe: SkolemType => pickleType(tpe.info) case tpe: RefinedType => - val args = tpe.argInfos - if (args.isEmpty) { - writeByte(REFINEDtype) - withLength { - pickleType(tpe.parent) - pickleName(tpe.refinedName) - pickleType(tpe.refinedInfo, richTypes = true) - } - } - else { - writeByte(APPLIEDtype) - withLength { pickleType(tpe.withoutArgs(args)); args.foreach(pickleType(_)) } + writeByte(REFINEDtype) + withLength { + pickleType(tpe.parent) + pickleName(tpe.refinedName) + pickleType(tpe.refinedInfo, richTypes = true) } case tpe: TypeAlias => writeByte(TYPEALIAS) diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 747d73ea9..b0e31202f 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -53,7 +53,7 @@ object Scala2Unpickler { case TempPolyType(tparams, restpe) => if (denot.isType) { assert(!denot.isClass) - restpe.LambdaAbstract(tparams, cycleParanoid = true) + restpe.LambdaAbstract(tparams) } else PolyType.fromSymbols(tparams, restpe) @@ -715,8 +715,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas else TypeRef(pre, sym.name.asTypeName) val args = until(end, readTypeRef) if (sym == defn.ByNameParamClass2x) ExprType(args.head) - else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams) - else tycon.appliedTo(adaptArgs(sym.typeParams, args)) + else if (args.nonEmpty) tycon.safeAppliedTo(adaptArgs(sym.typeParams, args)) + else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams) + else tycon case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef()) case REFINEDtpe => diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index e8ba3b07b..d119658be 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -614,10 +614,10 @@ trait Applications extends Compatibility { self: Typer => } def adaptTypeArg(tree: tpd.Tree, bound: Type)(implicit ctx: Context): tpd.Tree = { - val was = tree.tpe.EtaExpandIfHK(bound) + //val was = tree.tpe.EtaExpandIfHK(bound) //val now = tree.tpe.adaptIfHK(bound) // ### //if (was != now) println(i"diff adapt ${tree.tpe} to $bound, was: $was, now: $now") - tree.withType(was)//tree.tpe.adaptIfHK(bound)) + tree.withType(tree.tpe.adaptIfHK(bound)) } /** Rewrite `new Array[T](....)` trees to calls of newXYZArray methods. */ diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 3f5c4f47e..f857a2504 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -348,10 +348,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => var tpt1 = typedType(tree.tpt) - if (tpt1.tpe.isHK) { - val deAliased = tpt1.tpe.dealias.EtaReduce - if (deAliased.exists && deAliased.ne(tpt1.tpe)) - tpt1 = tpt1.withType(deAliased) + tpt1.tpe.dealias match { + case TypeApplications.EtaExpansion(tycon) => tpt1 = tpt1.withType(tycon) + case _ => } checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) assignType(cpy.New(tree)(tpt1), tpt1) diff --git a/tests/pos/t2693.scala b/tests/pos/t2693.scala index 5d4d0380c..537e6d8ab 100644 --- a/tests/pos/t2693.scala +++ b/tests/pos/t2693.scala @@ -1,6 +1,6 @@ class A { - trait T[A] + trait Tr[A] def usetHk[T[_], A](ta: T[A]) = 0 - usetHk(new T[Int]{}: T[Int]) - usetHk(new T[Int]{}) // fails with: found: java.lang.Object with T[Int], required: ?T[ ?A ] + usetHk(new Tr[Int]{}: Tr[Int]) + usetHk(new Tr[Int]{}) // fails with: found: java.lang.Object with T[Int], required: ?T[ ?A ] } -- cgit v1.2.3 From f6d1153e193045ad4d8a1564e3c0cdb49b853a5b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 6 Dec 2015 12:44:00 +0100 Subject: Avoid false positives when extracting AppliedType --- src/dotty/tools/dotc/core/TypeApplications.scala | 8 +++++++- src/dotty/tools/dotc/core/Types.scala | 12 +++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index db7550402..ab15b3e1a 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -133,7 +133,13 @@ object TypeApplications { def unapply(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { case TypeRef(prefix, tpnme.hkApply) => unapp(prefix) - case _ => unapp(tp) + case _ => + unapp(tp) match { + case Some((tycon: TypeRef, _)) if tycon.symbol.isLambdaTrait => + // We are seeing part of a lambda abstraction, not an applied type + None + case x => x + } } private def unapp(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 54e6397bb..b5d5c864b 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1899,7 +1899,17 @@ object Types { override def underlying(implicit ctx: Context) = parent - private def checkInst(implicit ctx: Context): this.type = this + private def badInst = + throw new AssertionError(s"bad instantiation: $this") + + def checkInst(implicit ctx: Context): this.type = { + if (refinedName == tpnme.hkApply) + parent.stripTypeVar match { + case RefinedType(_, name) if name.isHkArgName => // ok + case _ => badInst + } + this + } def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): RefinedType = if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this -- cgit v1.2.3 From 88f24efb2858f46c146214bc7e51f5de17c31bc0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 Dec 2015 14:39:41 +0100 Subject: Allow bottom types as hk type arguments Fixes problem raised in #966. --- src/dotty/tools/dotc/core/TypeApplications.scala | 10 +++++++--- src/dotty/tools/dotc/core/Types.scala | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index dabd2f2cd..b2f22da20 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -328,10 +328,14 @@ class TypeApplications(val self: Type) extends AnyVal { //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } - /** Eta expand the prefix in front of any refinements. */ - def EtaExpandCore(implicit ctx: Context): Type = self.stripTypeVar match { + /** Eta expand the prefix in front of any refinements. + * @param tparamsForBottom Type parameters to use if core is a bottom type + */ + def EtaExpandCore(tparamsForBottom: List[TypeSymbol])(implicit ctx: Context): Type = self.stripTypeVar match { case self: RefinedType => - self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo) + self.derivedRefinedType(self.parent.EtaExpandCore(tparamsForBottom), self.refinedName, self.refinedInfo) + case tp: TypeRef if defn.isBottomClass(tp.symbol) => + self.LambdaAbstract(tparamsForBottom) case _ => self.EtaExpand(self.typeParams) } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index b5d5c864b..a7c457d8f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1517,7 +1517,7 @@ object Types { // After substitution we might end up with a type like // `C { type hk$0 = T0; ...; type hk$n = Tn } # $Apply` // where C is a class. In that case we eta expand `C`. - derivedSelect(prefix.EtaExpandCore) + derivedSelect(prefix.EtaExpandCore(this.prefix.typeConstructor.typeParams)) } else newLikeThis(prefix) } -- cgit v1.2.3 From dba4b94443a8066df23ee7ccdb890ad048a06a2c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 13 Dec 2015 15:32:17 +0100 Subject: Avoid cycle when computing sets The tightened subtyping algorithm led to a cycle in baseTypeRef when compiling sets.scala. This commit fixes the problem. --- src/dotty/tools/dotc/core/Types.scala | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index a7c457d8f..f65633595 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -447,7 +447,18 @@ object Types { if (rinfo.isAlias) rinfo else if (pdenot.info.isAlias) pdenot.info else if (ctx.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo) - else pdenot.info & rinfo + else + try pdenot.info & rinfo + catch { + case ex: CyclicReference => + // happens for tests/pos/sets.scala. findMember is called from baseTypeRef. + // The & causes a subtype check which calls baseTypeRef again with the same + // superclass. In the observed case, the superclass was Any, and + // the special shortcut for Any in derivesFrom was as yet absent. To reproduce, + // remove the special treatment of Any in derivesFrom and compile + // sets.scala. + safeAnd(pdenot.info, rinfo) + } pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) } else pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre) @@ -1562,6 +1573,15 @@ object Types { case _ => false } + + /* A version of toString which also prints aliases. Can be used for debugging + override def toString = + if (isTerm) s"TermRef($prefix, $name)" + else s"TypeRef($prefix, $name)${ + if (lastDenotation != null && lastDenotation.infoOrCompleter.isAlias) + s"@@@ ${lastDenotation.infoOrCompleter.asInstanceOf[TypeAlias].hi}" + else ""}" + */ } abstract case class TermRef(override val prefix: Type, name: TermName) extends NamedType with SingletonType { -- cgit v1.2.3 From 95098ba498b5317002395008376a565550189673 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 13 Dec 2015 15:34:38 +0100 Subject: Shortcut in derivesFrom for high bound Any. Any is a supertype of every other type, so no need to analyze types in detail. This also fixes the cyclic reference error observed for sets.scala, but only for the special case where the base class is Any. --- src/dotty/tools/dotc/core/Types.scala | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index f65633595..596d1ba24 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -141,20 +141,23 @@ object Types { } /** Is this type an instance of a non-bottom subclass of the given class `cls`? */ - final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = this match { - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) sym.derivesFrom(cls) else tp.underlying.derivesFrom(cls) - case tp: TypeProxy => - tp.underlying.derivesFrom(cls) - case tp: AndType => - tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls) - case tp: OrType => - tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls) - case tp: JavaArrayType => - cls == defn.ObjectClass - case _ => - false + final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = { + def loop(tp: Type) = tp match { + case tp: TypeRef => + val sym = tp.symbol + if (sym.isClass) sym.derivesFrom(cls) else tp.underlying.derivesFrom(cls) + case tp: TypeProxy => + tp.underlying.derivesFrom(cls) + case tp: AndType => + tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls) + case tp: OrType => + tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls) + case tp: JavaArrayType => + cls == defn.ObjectClass + case _ => + false + } + cls == defn.AnyClass || loop(this) } /** Is this type guaranteed not to have `null` as a value? -- cgit v1.2.3 From ac99941234da48427d1ca10823e45aa740baa354 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 8 Dec 2015 20:16:03 +0100 Subject: Do not report data races between symbols defined in class and its selftype Analogous to the previous situation where we do not report a data race if the previous symbol comes from a superclass, we now do the same if the previous symbol comes from a given self type. Makes overrideDataRace.scala pass, and finally enables stdlib test with TraverableViewLike.scala added. --- src/dotty/tools/dotc/core/Symbols.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 14 ++++++++++---- test/dotc/scala-collections.whitelist | 2 +- tests/pos/overrideDataRace.scala | 13 +++++++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 tests/pos/overrideDataRace.scala (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index e33f9651e..2a76f18d8 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -368,7 +368,7 @@ object Symbols { type ThisName <: Name - //assert(_id != 30214) + //assert(id != 4285) /** The last denotation of this symbol */ private[this] var lastDenot: SymDenotation = _ diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 596d1ba24..710d45e24 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1386,7 +1386,11 @@ object Types { if (owner.isTerm) d else d.asSeenFrom(prefix) } - private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = + private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = { + def ownerSelfType = sym.owner.info match { + case info: ClassInfo => info.givenSelfType + case _ => NoType + } assert( (lastSymbol eq sym) || (lastSymbol eq null) || { @@ -1398,9 +1402,11 @@ object Types { (lastDefRunId == NoRunId) } || (lastSymbol.infoOrCompleter == ErrorType || - sym.owner.derivesFrom(lastSymbol.owner) && sym.owner != lastSymbol.owner - ), - s"data race? overwriting symbol of ${this.show} / $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id} / ${sym.owner} / ${lastSymbol.owner} / ${ctx.phase} at run ${ctx.runId}") + sym.owner != lastSymbol.owner && + (sym.owner.derivesFrom(lastSymbol.owner) || + ownerSelfType.derivesFrom(lastSymbol.owner))), + s"data race? overwriting symbol of ${this.show} / $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id} / ${lastSymbol.owner} / ${sym.owner} / ${ctx.phase} at run ${ctx.runId}") + } protected def sig: Signature = Signature.NotAMethod diff --git a/test/dotc/scala-collections.whitelist b/test/dotc/scala-collections.whitelist index 39db99c79..5c3c38e66 100644 --- a/test/dotc/scala-collections.whitelist +++ b/test/dotc/scala-collections.whitelist @@ -181,7 +181,7 @@ ## those classes use early initialisers. #./scala-scala/src/library/scala/collection/IterableViewLike.scala #./scala-scala/src/library/scala/collection/SeqViewLike.scala -#./scala-scala/src/library/scala/collection/TraversableViewLike.scala +./scala-scala/src/library/scala/collection/TraversableViewLike.scala #./scala-scala/src/library/scala/collection/immutable/StreamViewLike.scala ## This class causes a crash in backend. diff --git a/tests/pos/overrideDataRace.scala b/tests/pos/overrideDataRace.scala new file mode 100644 index 000000000..830ffd668 --- /dev/null +++ b/tests/pos/overrideDataRace.scala @@ -0,0 +1,13 @@ +package test + +trait Traversable { + def mkString: String = ??? +} + +trait ViewMkString { + self: Traversable => + + def mkString: String = mkString("") + def mkString(s: String) = ??? + +} -- cgit v1.2.3 From 8203177068d02327e6c8ca576d2c704204500e27 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 10 Dec 2015 11:09:36 +0100 Subject: Adaptations to checkSymAssign (1) Also allow symbols to switch between a member of a class and a member of its selftype. Seen in the wild in TraversableViewLike.scala. Test case in pickling/selfSym.scala (2) Tidy up the error message. --- src/dotty/tools/dotc/core/Types.scala | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 710d45e24..159e776b4 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1387,7 +1387,7 @@ object Types { } private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = { - def ownerSelfType = sym.owner.info match { + def selfTypeOf(sym: Symbol) = sym.owner.info match { case info: ClassInfo => info.givenSelfType case _ => NoType } @@ -1404,8 +1404,13 @@ object Types { (lastSymbol.infoOrCompleter == ErrorType || sym.owner != lastSymbol.owner && (sym.owner.derivesFrom(lastSymbol.owner) || - ownerSelfType.derivesFrom(lastSymbol.owner))), - s"data race? overwriting symbol of ${this.show} / $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id} / ${lastSymbol.owner} / ${sym.owner} / ${ctx.phase} at run ${ctx.runId}") + selfTypeOf(sym).derivesFrom(lastSymbol.owner) || + selfTypeOf(lastSymbol).derivesFrom(sym.owner))), + s"""data race? overwriting symbol of type ${this.show}, + |long form = $this of class ${this.getClass}, + |last sym id = ${lastSymbol.id}, new sym id = ${sym.id}, + |last owner = ${lastSymbol.owner}, new owner = ${sym.owner}, + |period = ${ctx.phase} at run ${ctx.runId}""".stripMargin) } protected def sig: Signature = Signature.NotAMethod -- cgit v1.2.3 From 3476bab33e9509963fd02aab10b4db3b891c9a85 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 10 Dec 2015 15:37:37 +0100 Subject: Better diagnostics for clashing classes We do not allow same-named class members in supertraits of a mixin composition anymore. This commit gives a better error message and avoids a crash in RefChecks. --- src/dotty/tools/dotc/core/Denotations.scala | 2 +- src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- src/dotty/tools/dotc/typer/RefChecks.scala | 19 +++++++++++++++---- 4 files changed, 18 insertions(+), 7 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index b6519e1cd..fcd60ef16 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -324,7 +324,7 @@ object Denotations { info1 // follow Scala2 linearization - // compare with way merge is performed in SymDenotation#computeMembersNamed else - throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}") + throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2) } new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor) } diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index f8ff84ba1..71dede951 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1168,7 +1168,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case bounds: TypeBounds => i"type bounds $bounds" case _ => tp.show } - throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") + throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2) } /** Merge two lists of names. If names in corresponding positions match, keep them, diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 159e776b4..0691d979a 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -3318,7 +3318,7 @@ object Types { } } - class MergeError(msg: String) extends TypeError(msg) + class MergeError(msg: String, val tp1: Type, val tp2: Type) extends TypeError(msg) // ----- Debug --------------------------------------------------------- diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 3ffbb8079..416a2cb92 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -15,6 +15,7 @@ import TreeTransforms._ import util.DotClass import scala.util.{Try, Success, Failure} import config.{ScalaVersion, NoScalaVersion} +import Decorators._ import typer.ErrorReporting._ import DenotTransformers._ import ValueClasses.isDerivedValueClass @@ -340,10 +341,20 @@ object RefChecks { }*/ } - val opc = new OverridingPairs.Cursor(clazz) - while (opc.hasNext) { - checkOverride(opc.overriding, opc.overridden) - opc.next() + try { + val opc = new OverridingPairs.Cursor(clazz) + while (opc.hasNext) { + checkOverride(opc.overriding, opc.overridden) + opc.next() + } + } catch { + case ex: MergeError => + val addendum = ex.tp1 match { + case tp1: ClassInfo => + "\n(Note that having same-named member classes in types of a mixin composition is no longer allowed)" + case _ => "" + } + ctx.error(ex.getMessage + addendum, clazz.pos) } printMixinOverrideErrors() -- cgit v1.2.3 From 86e35e48bb7916b6c2e35147973d2572a29b93c3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 14 Dec 2015 17:15:32 +0100 Subject: Use symbolic refs when testing whether a TypeBounds contains a ClassInfo Without the fix and the later commit that checks types for overriding we get a Ycheck failure in t3452h.scala. --- src/dotty/tools/dotc/core/TypeComparer.scala | 11 +++++------ src/dotty/tools/dotc/core/Types.scala | 12 +++++++++--- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'src/dotty/tools/dotc/core/Types.scala') diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 71dede951..163fa4919 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -434,8 +434,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { (tp2.variance > 0 && tp1.variance >= 0 || (lo2 eq NothingType) || isSubType(lo2, lo1)) && (tp2.variance < 0 && tp1.variance <= 0 || (hi2 eq AnyType) || isSubType(hi1, hi2)) case tp1: ClassInfo => - val tt = tp1.typeRef - isSubType(lo2, tt) && isSubType(tt, hi2) + tp2 contains tp1 case _ => false } @@ -1063,13 +1062,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: TypeBounds => tp2 match { case tp2: TypeBounds => tp1 & tp2 - case tp2: ClassInfo if tp1 contains tp2.typeRef => tp2 + case tp2: ClassInfo if tp1 contains tp2 => tp2 case _ => mergeConflict(tp1, tp2) } case tp1: ClassInfo => tp2 match { case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix) - case tp2: TypeBounds if tp2 contains tp1.typeRef => tp1 + case tp2: TypeBounds if tp2 contains tp1 => tp1 case _ => mergeConflict(tp1, tp2) } case tp1 @ MethodType(names1, formals1) => @@ -1122,13 +1121,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: TypeBounds => tp2 match { case tp2: TypeBounds => tp1 | tp2 - case tp2: ClassInfo if tp1 contains tp2.typeRef => tp1 + case tp2: ClassInfo if tp1 contains tp2 => tp1 case _ => mergeConflict(tp1, tp2) } case tp1: ClassInfo => tp2 match { case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix | tp2.prefix) - case tp2: TypeBounds if tp2 contains tp1.typeRef => tp2 + case tp2: TypeBounds if tp2 contains tp1 => tp2 case _ => mergeConflict(tp1, tp2) } case tp1 @ MethodType(names1, formals1) => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 0691d979a..21b74e07b 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2663,11 +2663,13 @@ object Types { def clsDenot = if (prefix eq cls.owner.thisType) cls.denot else cls.denot.copySymDenotation(info = this) if (typeRefCache == null) typeRefCache = - if ((cls is PackageClass) || cls.owner.isTerm) prefix select cls - else prefix select (cls.name, clsDenot) + if ((cls is PackageClass) || cls.owner.isTerm) symbolicTypeRef + else TypeRef(prefix, cls.name, clsDenot) typeRefCache } + def symbolicTypeRef(implicit ctx: Context): Type = TypeRef(prefix, cls) + // cached because baseType needs parents private var parentsCache: List[TypeRef] = null @@ -2732,8 +2734,12 @@ object Types { case _ => this } - def contains(tp: Type)(implicit ctx: Context) = tp match { + def contains(tp: Type)(implicit ctx: Context): Boolean = tp match { case tp: TypeBounds => lo <:< tp.lo && tp.hi <:< hi + case tp: ClassInfo => + // Note: Taking a normal typeRef does not work here. A normal ref might contain + // also other information about the named type (e.g. bounds). + contains(tp.symbolicTypeRef) case _ => lo <:< tp && tp <:< hi } -- cgit v1.2.3