Skip to content

Commit 82a1721

Browse files
committed
Refinements to realizability
Unstable types can still be realizable (i.e. if their underlying type is concrete with no bad bounds).
1 parent 0c18756 commit 82a1721

File tree

6 files changed

+57
-12
lines changed

6 files changed

+57
-12
lines changed

compiler/src/dotty/tools/dotc/core/CheckRealizable.scala

+10-4
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,16 @@ class CheckRealizable(implicit ctx: Context) {
7373
if (sym.is(Stable)) realizability(tp.prefix)
7474
else {
7575
val r =
76-
if (!sym.isStable) NotStable
77-
else if (!isLateInitialized(sym)) Realizable
78-
else if (!sym.isEffectivelyFinal) new NotFinal(sym)
79-
else realizability(tp.info).mapError(r => new ProblemInUnderlying(tp.info, r))
76+
if (sym.isStable && !isLateInitialized(sym))
77+
// it's realizable because we know that a value of type `tp` has been created at run-time
78+
Realizable
79+
else if (!sym.isEffectivelyFinal)
80+
// it's potentially not realizable since it might be overridden with a member of nonrealizable type
81+
new NotFinal(sym)
82+
else
83+
// otherwise we need to look at the info to determine realizability
84+
// roughly: it's realizable if the info does not have bad bounds
85+
realizability(tp.info).mapError(r => new ProblemInUnderlying(tp, r))
8086
r andAlso {
8187
sym.setFlag(Stable)
8288
realizability(tp.prefix)

compiler/src/dotty/tools/dotc/core/Types.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ object Types {
149149

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

4509+
protected def emptyRange = range(defn.NothingType, defn.AnyType)
4510+
45094511
protected def isRange(tp: Type): Boolean = tp.isInstanceOf[Range]
45104512

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

46324634
override protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type): Type =
4633-
if (isRange(thistp) || isRange(supertp)) range(defn.NothingType, defn.AnyType)
4635+
if (isRange(thistp) || isRange(supertp)) emptyRange
46344636
else tp.derivedSuperType(thistp, supertp)
46354637

46364638
override protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type =

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ trait TypeAssigner {
105105
case info: ClassInfo =>
106106
range(defn.NothingType, apply(classBound(info)))
107107
case _ =>
108-
range(defn.NothingType, defn.AnyType) // should happen only in error cases
108+
emptyRange // should happen only in error cases
109109
}
110110
case tp: ThisType if toAvoid(tp.cls) =>
111111
range(defn.NothingType, apply(classBound(tp.cls.classInfo)))

tests/neg/i50-volatile.scala

+12-5
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,23 @@ class Test {
1010
}
1111
lazy val o: A & B = ???
1212

13-
class Client extends o.Inner // old-error // old-error
13+
class Client extends o.Inner // error
1414

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

1717
def intToString(i: Int): String = xToString(i)
1818
}
19-
object Test2 {
2019

21-
import Test.o._ // error
20+
object Test2 {
21+
trait A {
22+
type X = String
23+
}
24+
trait B {
25+
type X = Int
26+
}
27+
lazy val o: A & B = ???
2228

23-
def xToString(x: X): String = x
29+
def xToString(x: o.X): String = x // error
2430

31+
def intToString(i: Int): String = xToString(i)
2532
}

tests/neg/realizability.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class C { type T }
2+
3+
class Test {
4+
5+
type D <: C
6+
7+
lazy val a: C = ???
8+
final lazy val b: C = ???
9+
val c: D = ???
10+
final lazy val d: D = ???
11+
12+
val x1: a.T = ??? // error: not a legal path, since a is lazy & non-final
13+
val x2: b.T = ??? // OK, b is lazy but concrete
14+
val x3: c.T = ??? // OK, c is abstract but strict
15+
val x4: d.T = ??? // error: not a legal path since d is abstract and lazy
16+
17+
val y1: Singleton = a
18+
val y2: Singleton = a
19+
val y3: Singleton = a
20+
val y4: Singleton = a
21+
22+
}

tests/pos/i4031.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object App {
2+
trait A { type L >: Any}
3+
def upcast(a: A, x: Any): a.L = x
4+
lazy val p: A { type L <: Nothing } = p
5+
val q = new A { type L = Any }
6+
def coerce1(x: Any): Any = upcast(q, x) // ok
7+
def coerce3(x: Any): Any = upcast(p, x) // ok, since dependent result type is not needed
8+
}

0 commit comments

Comments
 (0)