aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-09-30 10:07:41 +0200
committerMartin Odersky <odersky@gmail.com>2013-09-30 10:11:55 +0200
commite28dd856f6c0ccbd4094a73311a9c820f73913c4 (patch)
tree31acfaf5d391cb37db486c4bd585520bb228c165
parent968b608ea6f2d42d48f30e311d6008600dad27a8 (diff)
downloaddotty-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.scala37
-rw-r--r--src/dotty/tools/dotc/core/TyperState.scala1
-rw-r--r--src/dotty/tools/dotc/core/Types.scala92
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala3
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala10
-rw-r--r--tests/pos/inferred.scala25
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