Skip to content

Improve usability of erased values #4759

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

Merged
merged 4 commits into from
Jul 4, 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
10 changes: 6 additions & 4 deletions compiler/src/dotty/tools/dotc/core/CheckRealizable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ object CheckRealizable {

def boundsRealizability(tp: Type)(implicit ctx: Context) =
new CheckRealizable().boundsRealizability(tp)

private val LateInitialized = Lazy | Erased,
}

/** Compute realizability status */
Expand All @@ -63,10 +65,10 @@ class CheckRealizable(implicit ctx: Context) {
*/
private val checkedFields: mutable.Set[Symbol] = mutable.LinkedHashSet[Symbol]()

/** Is symbol's definitition a lazy val?
/** Is symbol's definitition a lazy or erased val?
* (note we exclude modules here, because their realizability is ensured separately)
*/
private def isLateInitialized(sym: Symbol) = sym.is(Lazy, butNot = Module)
private def isLateInitialized(sym: Symbol) = sym.is(LateInitialized, butNot = Module)

/** The realizability status of given type `tp`*/
def realizability(tp: Type): Realizability = tp.dealias match {
Expand Down Expand Up @@ -156,10 +158,10 @@ class CheckRealizable(implicit ctx: Context) {
private def memberRealizability(tp: Type) = {
def checkField(sofar: Realizability, fld: SingleDenotation): Realizability =
sofar andAlso {
if (checkedFields.contains(fld.symbol) || fld.symbol.is(Private | Mutable | Lazy))
if (checkedFields.contains(fld.symbol) || fld.symbol.is(Private | Mutable | Lazy | Erased))
// if field is private it cannot be part of a visible path
// if field is mutable it cannot be part of a path
// if field is lazy it does not need to be initialized when the owning object is
// if field is lazy or erased it does not need to be initialized when the owning object is
// so in all cases the field does not influence realizability of the enclosing object.
Realizable
else {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ object SymDenotations {

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

/** Is this a "real" method? A real method is a method which is:
* - not an accessor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class ExpandPrivate extends MiniPhase with IdentityDenotTransformer { thisPhase
}

assert(d.symbol.sourceFile != null &&
ctx.owner.sourceFile != null &&
isSimilar(d.symbol.sourceFile.path, ctx.owner.sourceFile.path),
s"private ${d.symbol.showLocated} in ${d.symbol.sourceFile} accessed from ${ctx.owner.showLocated} in ${ctx.owner.sourceFile}")
d.ensureNotPrivate.installAfter(thisPhase)
Expand Down
17 changes: 3 additions & 14 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
}
app match {
case Apply(fun, args) if fun.tpe.widen.isErasedMethod =>
tpd.cpy.Apply(app)(fun = fun, args = args.map(arg => normalizeErasedExpr(arg, "This argument is given to an erased parameter. ")))
tpd.cpy.Apply(app)(fun = fun, args = args.map(arg => defaultValue(arg.tpe)))
case _ => app
}
}
Expand Down Expand Up @@ -1567,22 +1567,11 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
harmonizedElems
}

/** Transforms the tree into a its default tree.
* Performed to shrink the tree that is known to be erased later.
*/
protected def normalizeErasedExpr(tree: Tree, msg: String)(implicit ctx: Context): Tree = {
if (!isPureExpr(tree))
ctx.warning(msg + "This expression will not be evaluated.", tree.pos)
defaultValue(tree.tpe)
}

/** Transforms the rhs tree into a its default tree if it is in an `erased` val/def.
* Performed to shrink the tree that is known to be erased later.
*/
protected def normalizeErasedRhs(rhs: Tree, sym: Symbol)(implicit ctx: Context) = {
if (sym.is(Erased) && rhs.tpe.exists) normalizeErasedExpr(rhs, "Expression is on the RHS of an `erased` " + sym.showKind + ". ")
else rhs
}
protected def normalizeErasedRhs(rhs: Tree, sym: Symbol)(implicit ctx: Context) =
if (sym.is(Erased) && rhs.tpe.exists) defaultValue(rhs.tpe) else rhs

/** If all `types` are numeric value types, and they are not all the same type,
* pick a common numeric supertype and widen any constant types in `tpes` to it.
Expand Down
6 changes: 3 additions & 3 deletions tests/neg/erased-24.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ object Test {
println(fun(new Bar))
}

def fun(erased foo: Foo): foo.X = { // error
null.asInstanceOf[foo.X] // error
def fun(erased foo: Foo): foo.X = { // ok
null.asInstanceOf[foo.X] // ok
}

def fun2(erased foo: Foo)(erased bar: foo.B): bar.X = { // error // error
def fun2(erased foo: Foo)(erased bar: foo.B): bar.X = { // error
null.asInstanceOf[bar.X] // error
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/i4060.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class X { type R }
class T(erased val a: X)(val value: a.R) // error
class T(erased val a: X)(val value: a.R)

object App {
def coerce[U, V](u: U): V = {
Expand All @@ -8,7 +8,7 @@ object App {

class T[A <: X](erased val a: A)(val value: a.R) // error

object O { lazy val x : Y & X = ??? } // warning
object O { lazy val x : Y & X = ??? }

val a = new T[Y & X](O.x)(u)
a.value
Expand Down