diff options
author | Martin Odersky <odersky@gmail.com> | 2013-09-30 10:07:41 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-09-30 10:11:55 +0200 |
commit | e28dd856f6c0ccbd4094a73311a9c820f73913c4 (patch) | |
tree | 31acfaf5d391cb37db486c4bd585520bb228c165 | |
parent | 968b608ea6f2d42d48f30e311d6008600dad27a8 (diff) | |
download | dotty-e28dd856f6c0ccbd4094a73311a9c820f73913c4.tar.gz dotty-e28dd856f6c0ccbd4094a73311a9c820f73913c4.tar.bz2 dotty-e28dd856f6c0ccbd4094a73311a9c820f73913c4.zip |
Changed logic for merges in lub/glb
Now throws an exception for merge conflicts of types joint by |, a warning for types joint by &.
Exception is handled as in the case of CyclicRefernce.
Also: Added several typerState.checkConsistent for future debugging purposes.
-rw-r--r-- | src/dotty/tools/dotc/core/TypeComparer.scala | 37 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TyperState.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 92 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 10 | ||||
-rw-r--r-- | tests/pos/inferred.scala | 25 |
6 files changed, 104 insertions, 64 deletions
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 1d853c44d..13b900e18 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3,6 +3,7 @@ package dotc package core import Types._, Contexts._, Symbols._, Flags._ +import Decorators.sourcePos import StdNames.nme import collection.mutable import util.SimpleMap @@ -159,7 +160,7 @@ class TypeComparer(initctx: Context) extends DotClass { case tp2: PolyParam => //println(constraint.show) constraint(tp2) match { - case TypeBounds(lo, _) => isSubType(tp1, lo) || addConstraint(tp2, TypeBounds.lower(tp1)) + case TypeBounds(lo, _) => isSubType(tp1, lo) || addConstraint(tp2, TypeBounds.lower(tp1.widen)) case _ => secondTry(tp1, tp2) } case tp2: TypeVar => @@ -409,6 +410,8 @@ class TypeComparer(initctx: Context) extends DotClass { else tp2 match { // normalize to disjunctive normal form if possible. case OrType(tp21, tp22) => tp1 & tp21 | tp1 & tp22 +// case tp2: PolyType => +// mergePoly(tp2, tp1, lower = true) case _ => tp1 match { case OrType(tp11, tp12) => @@ -426,11 +429,11 @@ class TypeComparer(initctx: Context) extends DotClass { case tp2: TypeBounds => return TypeBounds(tp1.lo | tp2.lo, tp1.hi & tp2.hi) case tp2: ClassInfo => - return classMerge(tp2, tp1) + return classMerge(tp2, tp1, isAnd = true) case _ => } case tp1: ClassInfo => - return classMerge(tp1, tp2) + return classMerge(tp1, tp2, isAnd = true) case _ => } AndType(tp1, tp2) @@ -459,11 +462,11 @@ class TypeComparer(initctx: Context) extends DotClass { case tp2: TypeBounds => return TypeBounds(tp1.lo & tp2.lo, tp1.hi | tp2.hi) case tp2: ClassInfo => - return classMerge(tp2, tp1) + return classMerge(tp2, tp1, isAnd = false) case _ => } case tp1: ClassInfo => - return classMerge(tp1, tp2) + return classMerge(tp1, tp2, isAnd = false) case _ => } OrType(tp1, tp2) @@ -529,9 +532,27 @@ class TypeComparer(initctx: Context) extends DotClass { * can also be arbitrarily instantiated. In both cases we need to * make sure that such types do not actually arise in source programs. */ - private def classMerge(cinfo: ClassInfo, tp2: Type)(implicit ctx: Context): Type = tp2 match { - case cinfo2: ClassInfo if isAsGood(cinfo2, cinfo) && !isAsGood(cinfo, cinfo2) => cinfo2 - case _ => cinfo + private def classMerge(cinfo: ClassInfo, tp2: Type, isAnd: Boolean)(implicit ctx: Context): Type = { + + def showTypeType(tp: Type)(implicit ctx: Context) = tp match { + case ClassInfo(_, cls, _, _, _) => cls.showLocated + case bounds: TypeBounds => "type bounds" + bounds.show + case _ => tp.show + } + + def msg = s"cannot merge ${showTypeType(cinfo)} with ${showTypeType(tp2)}" + val pos = ctx.tree.pos + + if (isAnd) { + val winner = tp2 match { + case cinfo2: ClassInfo if isAsGood(cinfo2, cinfo) && !isAsGood(cinfo, cinfo2) => cinfo2 + case _ => cinfo + } + ctx.warning(s"$msg as members of one type; keeping only ${showTypeType(winner)}", ctx.tree.pos) + winner + } + else + throw new ClassMergeError(msg) } private def isAsGood(cinfo1: ClassInfo, cinfo2: ClassInfo)(implicit ctx: Context): Boolean = diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index f7631fa22..4fcfa323e 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -78,6 +78,7 @@ extends TyperState(reporter) { targetState.instType = targetState.instType remove tvar } } + targetState.checkConsistent // !!! DEBUG reporter.flush() } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 59ac01819..d116a2426 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -374,49 +374,54 @@ object Types { * as seen from given prefix `pre`. Exclude all members that have * flags in `excluded` from consideration. */ - final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = this match { - case tp: RefinedType => - val pdenot = tp.parent.findMember(name, pre, excluded) - if (name eq tp.refinedName) { - val rinfo = tp.refinedInfo.substThis(tp, pre) - if (name.isTypeName) {// simplified case that runs more efficiently - val info = if (pdenot.symbol is TypeParam) rinfo else pdenot.info & rinfo - pdenot.asInstanceOf[SingleDenotation].derivedSingleDenotation( - pdenot.symbol, info) - } + final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = try { + this match { + case tp: RefinedType => + val pdenot = tp.parent.findMember(name, pre, excluded) + if (name eq tp.refinedName) { + val rinfo = tp.refinedInfo.substThis(tp, pre) + if (name.isTypeName) { // simplified case that runs more efficiently + val info = if (pdenot.symbol is TypeParam) rinfo else pdenot.info & rinfo + pdenot.asInstanceOf[SingleDenotation].derivedSingleDenotation( + pdenot.symbol, info) + } else + pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre) + } else pdenot + case tp: ThisType => + val d = tp.underlying.findMember(name, pre, excluded) + if (d.exists) d else - pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre) - } - else pdenot - case tp: ThisType => - val d = tp.underlying.findMember(name, pre, excluded) - if (d.exists) d - else - // There is a special case to handle: - // trait Super { this: Sub => private class Inner {} println(this.Inner) } - // class Sub extends Super - // When resolving Super.this.Inner, the normal logic goes to the self type and - // looks for Inner from there. But this fails because Inner is private. - // We fix the problem by having the following fallback case, which links up the - // member in Super instead of Sub. - // As an example of this in the wild, see - // loadClassWithPrivateInnerAndSubSelf in ShowClassTests - tp.cls.symTypeRef.findMember(name, pre, excluded) orElse d - case tp: TypeRef => - tp.denot.findMember(name, pre, excluded) - case tp: TypeProxy => - tp.underlying.findMember(name, pre, excluded) - case tp: ClassInfo => - tp.cls.findMember(name, pre, excluded) - case AndType(l, r) => - l.findMember(name, pre, excluded) & (r.findMember(name, pre, excluded), pre) - case OrType(l, r) => - l.findMember(name, pre, excluded) | (r.findMember(name, pre, excluded), pre) - case ErrorType => - ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name) - case _ => - NoDenotation - } /* !!! DEBUG ensuring { denot => + // There is a special case to handle: + // trait Super { this: Sub => private class Inner {} println(this.Inner) } + // class Sub extends Super + // When resolving Super.this.Inner, the normal logic goes to the self type and + // looks for Inner from there. But this fails because Inner is private. + // We fix the problem by having the following fallback case, which links up the + // member in Super instead of Sub. + // As an example of this in the wild, see + // loadClassWithPrivateInnerAndSubSelf in ShowClassTests + tp.cls.symTypeRef.findMember(name, pre, excluded) orElse d + case tp: TypeRef => + tp.denot.findMember(name, pre, excluded) + case tp: TypeProxy => + tp.underlying.findMember(name, pre, excluded) + case tp: ClassInfo => + tp.cls.findMember(name, pre, excluded) + case AndType(l, r) => + l.findMember(name, pre, excluded) & (r.findMember(name, pre, excluded), pre) + case OrType(l, r) => + l.findMember(name, pre, excluded) | (r.findMember(name, pre, excluded), pre) + case ErrorType => + ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name) + case _ => + NoDenotation + } + } catch { + case ex: ClassMergeError => + throw new ClassMergeError(s"${ex.getMessage} as members of type ${pre.show}") + } + + /* !!! DEBUG ensuring { denot => denot.alternatives forall (_.symbol.name == name) }*/ @@ -1824,6 +1829,7 @@ object Types { owningState.undetVars -= this if (ctx.typerState eq creatorState) inst = tp else ctx.typerState.instType = ctx.typerState.instType.updated(this, tp) + ctx.typerState.checkConsistent // !!! DEBUG tp } @@ -2354,6 +2360,8 @@ object Types { class CyclicReference(val denot: SymDenotation) extends FatalTypeError(s"cyclic reference involving $denot") + class ClassMergeError(msg: String) extends FatalTypeError(msg) + // ----- Debug --------------------------------------------------------- var debugTrace = false diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index 91bc769e9..52b856864 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -272,8 +272,11 @@ trait Implicits { self: Typer => /** Search a list of eligible implicit references */ def searchImplicits(eligible: List[TermRef], contextual: Boolean): SearchResult = { + ctx.typerState.checkConsistent // !!! DEBUG + /** Try to typecheck an implicit reference */ def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = { + ctx.typerState.checkConsistent // !!! DEBUG val id = Ident(ref).withPos(pos) val tree = if (argument.isEmpty) adapt(id, pt) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index ba99ed0ca..224b3fcb1 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -851,9 +851,13 @@ class Typer extends Namer with Applications with Implicits { } def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = ctx.traceIndented (s"typing ${tree.show}", show = true) { - val tree1 = typedUnadapted(tree, pt) - ctx.interpolateUndetVars(tree1.tpe.widen, tree1.pos) - adapt(tree1, pt) + try { + val tree1 = typedUnadapted(tree, pt) + ctx.interpolateUndetVars(tree1.tpe.widen, tree1.pos) + adapt(tree1, pt) + } catch { + case ex: FatalTypeError => errorTree(tree, ex.getMessage) + } } def typedTrees(trees: List[untpd.Tree])(implicit ctx: Context): List[Tree] = diff --git a/tests/pos/inferred.scala b/tests/pos/inferred.scala index 62d84c559..88038147c 100644 --- a/tests/pos/inferred.scala +++ b/tests/pos/inferred.scala @@ -1,22 +1,22 @@ -class List[+T] { +class LIST[+T] { def isEmpty: Boolean def head: T - def tail: List[T] + def tail: LIST[T] - def prepend [U >: T] (x: U): List[U] = new Cons(x, this) + def prepend [U >: T] (x: U): LIST[U] = new CONS(x, this) - def map[U](f: T => U): List[U] = if (isEmpty) Nil else tail.map(f).prepend(f(head)) + def map[U](f: T => U): LIST[U] = if (isEmpty) NIL else tail.map(f).prepend(f(head)) } -object Nil extends List[Nothing] { +object NIL extends LIST[Nothing] { def isEmpty = true def head = throw new Error def tail = ??? } -class Cons[T](hd: T, tl: List[T]) extends List[T] { +class CONS[T](hd: T, tl: LIST[T]) extends LIST[T] { def isEmpty = false def head = hd def tail = tl @@ -31,15 +31,17 @@ object Inferred { val y = foo("abc") - def bar[U](xs: List[U]): List[U] = xs + def bar[U](xs: LIST[U]): LIST[U] = xs - val n = Nil + val n = NIL - val nn = bar(Nil) + val nn = bar(NIL) - val ints: List[Int] = Nil prepend 1 + val ints: LIST[Int] = NIL prepend 1 + + val ints1 = NIL prepend 1 prepend 2 - val a = if (1 == 0) Nil else ints + val a = if (1 == 0) NIL else ints val n2 = scala.collection.immutable.Nil @@ -50,4 +52,5 @@ object Inferred { def cl = ((x: Int) => x + 1) val ints2 = ints map (_ + 1) + }
\ No newline at end of file |