From 7ccd02c2cd23e4187f3e3a378973704cecd6459a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 30 Jan 2016 20:01:14 +0100 Subject: Realizability refactoring Distinguish between isStable and isRealizable. Issue migration warnings for realizibility failures. Provide error diagnostics why something is not realizable. --- src/dotty/tools/dotc/core/Flags.scala | 2 +- src/dotty/tools/dotc/core/SymDenotations.scala | 16 +++++------ src/dotty/tools/dotc/core/Types.scala | 40 +++++++++++++++++++------- src/dotty/tools/dotc/typer/Checking.scala | 20 +++++++++---- src/dotty/tools/dotc/typer/Typer.scala | 7 +++-- 5 files changed, 58 insertions(+), 27 deletions(-) (limited to 'src/dotty/tools/dotc') 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, "") /** 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) } -- cgit v1.2.3