Skip to content

Commit

Permalink
Refinements to realizability
Browse files Browse the repository at this point in the history
Unstable types can still be realizable (i.e. if their underlying
type is concrete with no bad bounds).
  • Loading branch information
odersky committed Dec 5, 2018
1 parent 0c18756 commit 82a1721
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 12 deletions.
14 changes: 10 additions & 4 deletions compiler/src/dotty/tools/dotc/core/CheckRealizable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,16 @@ class CheckRealizable(implicit ctx: Context) {
if (sym.is(Stable)) realizability(tp.prefix)
else {
val r =
if (!sym.isStable) NotStable
else if (!isLateInitialized(sym)) Realizable
else if (!sym.isEffectivelyFinal) new NotFinal(sym)
else realizability(tp.info).mapError(r => new ProblemInUnderlying(tp.info, r))
if (sym.isStable && !isLateInitialized(sym))
// it's realizable because we know that a value of type `tp` has been created at run-time
Realizable
else if (!sym.isEffectivelyFinal)
// it's potentially not realizable since it might be overridden with a member of nonrealizable type
new NotFinal(sym)
else
// otherwise we need to look at the info to determine realizability
// roughly: it's realizable if the info does not have bad bounds
realizability(tp.info).mapError(r => new ProblemInUnderlying(tp, r))
r andAlso {
sym.setFlag(Stable)
realizability(tp.prefix)
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ object Types {

/** Does this type denote a stable reference (i.e. singleton type)? */
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable || tp.info.isStable
case tp: TermRef => tp.symbol.isStable && tp.prefix.isStable || tp.info.isStable
case _: SingletonType | NoPrefix => true
case tp: RefinedOrRecType => tp.parent.isStable
case tp: ExprType => tp.resultType.isStable
Expand Down Expand Up @@ -4506,6 +4506,8 @@ object Types {
else if (lo `eq` hi) lo
else Range(lower(lo), upper(hi))

protected def emptyRange = range(defn.NothingType, defn.AnyType)

protected def isRange(tp: Type): Boolean = tp.isInstanceOf[Range]

protected def lower(tp: Type): Type = tp match {
Expand Down Expand Up @@ -4630,7 +4632,7 @@ object Types {
else tp.derivedTypeBounds(lo, hi)

override protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type): Type =
if (isRange(thistp) || isRange(supertp)) range(defn.NothingType, defn.AnyType)
if (isRange(thistp) || isRange(supertp)) emptyRange
else tp.derivedSuperType(thistp, supertp)

override protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type =
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ trait TypeAssigner {
case info: ClassInfo =>
range(defn.NothingType, apply(classBound(info)))
case _ =>
range(defn.NothingType, defn.AnyType) // should happen only in error cases
emptyRange // should happen only in error cases
}
case tp: ThisType if toAvoid(tp.cls) =>
range(defn.NothingType, apply(classBound(tp.cls.classInfo)))
Expand Down
17 changes: 12 additions & 5 deletions tests/neg/i50-volatile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@ class Test {
}
lazy val o: A & B = ???

class Client extends o.Inner // old-error // old-error
class Client extends o.Inner // error

def xToString(x: o.X): String = x // old-error
def xToString(x: o.X): String = x // error

def intToString(i: Int): String = xToString(i)
}
object Test2 {

import Test.o._ // error
object Test2 {
trait A {
type X = String
}
trait B {
type X = Int
}
lazy val o: A & B = ???

def xToString(x: X): String = x
def xToString(x: o.X): String = x // error

def intToString(i: Int): String = xToString(i)
}
22 changes: 22 additions & 0 deletions tests/neg/realizability.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class C { type T }

class Test {

type D <: C

lazy val a: C = ???
final lazy val b: C = ???
val c: D = ???
final lazy val d: D = ???

val x1: a.T = ??? // error: not a legal path, since a is lazy & non-final
val x2: b.T = ??? // OK, b is lazy but concrete
val x3: c.T = ??? // OK, c is abstract but strict
val x4: d.T = ??? // error: not a legal path since d is abstract and lazy

val y1: Singleton = a
val y2: Singleton = a
val y3: Singleton = a
val y4: Singleton = a

}
8 changes: 8 additions & 0 deletions tests/pos/i4031.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object App {
trait A { type L >: Any}
def upcast(a: A, x: Any): a.L = x
lazy val p: A { type L <: Nothing } = p
val q = new A { type L = Any }
def coerce1(x: Any): Any = upcast(q, x) // ok
def coerce3(x: Any): Any = upcast(p, x) // ok, since dependent result type is not needed
}

0 comments on commit 82a1721

Please sign in to comment.