Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refinements to realizability #5568

Merged
merged 6 commits into from
Dec 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions compiler/src/dotty/tools/dotc/core/CheckRealizable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ object CheckRealizable {

object NotConcrete extends Realizability(" is not a concrete type")

object NotStable extends Realizability(" is not a stable reference")

class NotFinal(sym: Symbol)(implicit ctx: Context)
extends Realizability(i" refers to nonfinal $sym")

Expand Down Expand Up @@ -73,12 +71,18 @@ 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)
if (sym.isStable) sym.setFlag(Stable) // it's known to be stable and realizable
realizability(tp.prefix)
}
}
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -606,8 +606,10 @@ object SymDenotations {
)

/** Is this a denotation of a stable term (or an arbitrary type)? */
final def isStable(implicit ctx: Context): Boolean =
isType || is(StableOrErased) || !is(UnstableValue) && !info.isInstanceOf[ExprType]
final def isStable(implicit ctx: Context): Boolean = {
def isUnstableValue = is(UnstableValue) || info.isInstanceOf[ExprType]
isType || is(Stable) || !isUnstableValue
}

/** Is this a denotation of a class that does not have - either direct or inherited -
* initaliazion code?
Expand Down
14 changes: 7 additions & 7 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
thirdTry
case tp1: TypeParamRef =>
def flagNothingBound = {
if (!frozenConstraint && tp2.isRef(defn.NothingClass) && state.isGlobalCommittable) {
if (!frozenConstraint && tp2.isRef(NothingClass) && state.isGlobalCommittable) {
def msg = s"!!! instantiated to Nothing: $tp1, constraint = ${constraint.show}"
if (Config.failOnInstantiationToNothing) assert(false, msg)
else ctx.log(msg)
Expand Down Expand Up @@ -404,11 +404,12 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
if (cls2.isClass) {
if (cls2.typeParams.isEmpty) {
if (cls2 eq AnyKindClass) return true
if (tp1.isRef(defn.NothingClass)) return true
if (tp1.isRef(NothingClass)) return true
if (tp1.isLambdaSub) return false
// Note: We would like to replace this by `if (tp1.hasHigherKind)`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is now attached to the wrong line (it refers to isLambdaSub above)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed.

// but right now we cannot since some parts of the standard library rely on the
// idiom that e.g. `List <: Any`. We have to bootstrap without scalac first.
if (cls2 eq AnyClass) return true
if (cls2 == defn.SingletonClass && tp1.isStable) return true
return tryBaseType(cls2)
}
Expand All @@ -417,7 +418,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
val base = tp1.baseType(cls2)
if (base.typeSymbol == cls2) return true
}
else if (tp1.isLambdaSub && !tp1.isRef(defn.AnyKindClass))
else if (tp1.isLambdaSub && !tp1.isRef(AnyKindClass))
return recur(tp1, EtaExpansion(cls2.typeRef))
}
fourthTry
Expand Down Expand Up @@ -1382,7 +1383,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
// at run time. It would not work to replace that with `Nothing`.
// However, maybe we can still apply the replacement to
// types which are not explicitly written.
defn.NothingType
NothingType
case _ => andType(tp1, tp2)
}
case _ => andType(tp1, tp2)
Expand All @@ -1393,8 +1394,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {
}

/** The greatest lower bound of a list types */
final def glb(tps: List[Type]): Type =
((defn.AnyType: Type) /: tps)(glb)
final def glb(tps: List[Type]): Type = ((AnyType: Type) /: tps)(glb)

/** The least upper bound of two types
* @param canConstrain If true, new constraints might be added to simplify the lub.
Expand Down Expand Up @@ -1424,7 +1424,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling {

/** The least upper bound of a list of types */
final def lub(tps: List[Type]): Type =
((defn.NothingType: Type) /: tps)(lub(_,_, canConstrain = false))
((NothingType: Type) /: tps)(lub(_,_, canConstrain = false))

/** Try to produce joint arguments for a lub `A[T_1, ..., T_n] | A[T_1', ..., T_n']` using
* the following strategies:
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/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,7 @@ class RefChecks extends MiniPhase { thisPhase =>
checkAllOverrides(cls)
tree
} catch {
case ex: MergeError =>
case ex: TypeError =>
ctx.error(ex.getMessage, tree.pos)
tree
}
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
}