aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-01-30 20:01:14 +0100
committerMartin Odersky <odersky@gmail.com>2016-02-09 09:40:52 +0100
commit7ccd02c2cd23e4187f3e3a378973704cecd6459a (patch)
treec1efbbb4b0f73cc94b83c011bbcdc1b34e34ff9b /src/dotty/tools/dotc
parent1b7745e5f9c0e251436b33247133f3810838cf12 (diff)
downloaddotty-7ccd02c2cd23e4187f3e3a378973704cecd6459a.tar.gz
dotty-7ccd02c2cd23e4187f3e3a378973704cecd6459a.tar.bz2
dotty-7ccd02c2cd23e4187f3e3a378973704cecd6459a.zip
Realizability refactoring
Distinguish between isStable and isRealizable. Issue migration warnings for realizibility failures. Provide error diagnostics why something is not realizable.
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala2
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala16
-rw-r--r--src/dotty/tools/dotc/core/Types.scala40
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala20
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala7
5 files changed, 58 insertions, 27 deletions
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index 42d06c2ab..8c9db3a5c 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -300,7 +300,7 @@ object Flags {
*/
final val Abstract = commonFlag(23, "abstract")
- /** Method is assumed to be stable */
+ /** Lazy val or method is known or assumed to be stable and realizable */
final val Stable = termFlag(24, "<stable>")
/** A case parameter accessor */
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 3a66e10d4..fa199ab53 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -519,15 +519,15 @@ object SymDenotations {
)
/** Is this a denotation of a stable term (or an arbitrary type)? */
- final def isStable(implicit ctx: Context) = {
- val isUnstable =
- (this is UnstableValue) ||
- is(Lazy, butNot = Module) && !info.isRealizable && !hasAnnotation(defn.UncheckedStableAnnot)
- (this is Stable) || isType || {
- if (isUnstable) false
- else { setFlag(Stable); true }
+ final def isStable(implicit ctx: Context) =
+ isType || !is(UnstableValue, butNot = Stable)
+
+ /** Is this a denotation of a realizable term (or an arbitrary type)? */
+ final def isRealizable(implicit ctx: Context) =
+ is(Stable) || isType || {
+ val isRealizable = !is(Lazy, butNot = Module) || info.realizability == Realizable
+ isRealizable && { setFlag(Stable); true }
}
- }
/** Is this a "real" method? A real method is a method which is:
* - not an accessor
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 7e8a11e5b..0a5050148 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -127,10 +127,12 @@ object Types {
false
}
- /** Is this type realizable in all contexts? */
- def isRealizable(implicit ctx: Context): Boolean = dealias match {
- case tp: TermRef => tp.symbol.isStable
- case tp: SingletonType => true
+ /** The realizibility status of this type */
+ def realizability(implicit ctx: Context): Realizability = dealias match {
+ case tp: TermRef =>
+ if (tp.symbol.isRealizable) Realizable else NotStable
+ case _: SingletonType | NoPrefix =>
+ Realizable
case tp =>
def isConcrete(tp: Type): Boolean = tp.dealias match {
case tp: TypeRef => tp.symbol.isClass
@@ -138,12 +140,17 @@ object Types {
case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2)
case _ => false
}
- isConcrete(tp) &&
- tp.nonClassTypeMembers.forall { m =>
- val bounds = m.info.bounds
- bounds.lo <:< bounds.hi
- } ||
- ctx.scala2Mode
+ if (!isConcrete(tp)) NotConcrete
+ else {
+ def hasBadBounds(mbr: SingleDenotation) = {
+ val bounds = mbr.info.bounds
+ !(bounds.lo <:< bounds.hi)
+ }
+ tp.nonClassTypeMembers.find(hasBadBounds) match {
+ case Some(mbr) => new HasProblemBounds(mbr)
+ case _ => Realizable
+ }
+ }
}
/** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`?
@@ -3429,6 +3436,19 @@ object Types {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true
}
+ // ----- Realizibility Status -----------------------------------------------------
+
+ abstract class Realizability(val msg: String)
+
+ object Realizable extends Realizability("")
+
+ object NotConcrete extends Realizability("is not a concrete type")
+
+ object NotStable extends Realizability("is not a stable reference")
+
+ class HasProblemBounds(mbr: SingleDenotation)(implicit ctx: Context)
+ extends Realizability(i"has a member $mbr with possibly empty bounds ${mbr.info.bounds.lo} .. ${mbr.info.bounds.hi}")
+
// ----- Exceptions -------------------------------------------------------------
class TypeError(msg: String) extends Exception(msg)
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 0b4787d15..3a55352fd 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -318,9 +318,18 @@ trait Checking {
}
/** Check that type `tp` is stable. */
- def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
- if (!tp.isStable && !tp.isErroneous)
- ctx.error(d"$tp is not stable", pos)
+ def checkStableAndRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
+ if (!tp.isStable) ctx.error(d"$tp is not stable", pos)
+ else checkRealizable(tp, pos)
+
+ /** Check that type `tp` is realizable. */
+ def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = {
+ val rstatus = tp.realizability
+ if (rstatus ne Realizable) {
+ def msg = d"$tp is not a legal path since it ${rstatus.msg}"
+ if (ctx.scala2Mode) ctx.migrationWarning(msg, pos) else ctx.error(msg, pos)
+ }
+ }
/** Check that `tp` is a class type with a stable prefix. Also, if `traitReq` is
* true check that `tp` is a trait.
@@ -330,7 +339,7 @@ trait Checking {
def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type =
tp.underlyingClassRef(refinementOK = false) match {
case tref: TypeRef =>
- if (ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
+ if (ctx.phase <= ctx.refchecksPhase) checkStableAndRealizable(tref.prefix, pos)
if (traitReq && !(tref.symbol is Trait)) ctx.error(d"$tref is not a trait", pos)
tp
case _ =>
@@ -433,7 +442,8 @@ trait NoChecking extends Checking {
import tpd._
override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info
override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
- override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
+ override def checkStableAndRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
+ override def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp
override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index c94c90d1d..79035108e 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -308,7 +308,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") {
def asSelect(implicit ctx: Context): Tree = {
val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this))
- if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos)
+ if (tree.name.isTypeName) checkStableAndRealizable(qual1.tpe, qual1.pos)
typedSelect(tree, pt, qual1)
}
@@ -342,6 +342,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): Tree = track("typedSelectFromTypeTree") {
val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this))
+ //checkRealizable(qual1.tpe, qual1.pos)
assignType(cpy.SelectFromTypeTree(tree)(qual1, tree.name), qual1)
}
@@ -822,7 +823,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") {
val ref1 = typedExpr(tree.ref)
- checkStable(ref1.tpe, tree.pos)
+ checkStableAndRealizable(ref1.tpe, tree.pos)
assignType(cpy.SingletonTypeTree(tree)(ref1), ref1)
}
@@ -1056,7 +1057,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") {
val expr1 = typedExpr(imp.expr, AnySelectionProto)
- checkStable(expr1.tpe, imp.expr.pos)
+ checkStableAndRealizable(expr1.tpe, imp.expr.pos)
assignType(cpy.Import(imp)(expr1, imp.selectors), sym)
}