Skip to content

Ensure erased vals pass CheckRealizable #16111

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
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
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
checkNoConstructorProxy(tree)
transformSelect(tree, Nil)
case tree: Apply =>
val methType = tree.fun.tpe.widen
val methType = tree.fun.tpe.widen.asInstanceOf[MethodType]
val app =
if (methType.isErasedMethod)
tpd.cpy.Apply(tree)(
tree.fun,
tree.args.mapConserve(arg =>
if methType.isResultDependent then
Checking.checkRealizable(arg.tpe, arg.srcPos, "erased argument")
if (methType.isImplicitMethod && arg.span.isSynthetic)
arg match
case _: RefTree | _: Apply | _: TypeApply if arg.symbol.is(Erased) =>
Expand Down
6 changes: 6 additions & 0 deletions docs/_docs/reference/experimental/erased-defs-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,9 @@ TODO: complete
7. Overriding
* Member definitions overriding each other must both be `erased` or not be `erased`
* `def foo(x: T): U` cannot be overridden by `def foo(erased x: T): U` and vice-versa
*


8. Type Restrictions
* For dependent functions, `erased` parameters are limited to realizable types, that is, types that are inhabited by non-null values.
This restriction stops us from using a bad bound introduced by an erased value, which leads to unsoundness (see #4060).
21 changes: 21 additions & 0 deletions tests/neg-custom-args/erased/i4060.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// See https://github.com/lampepfl/dotty/issues/4060#issuecomment-445808377

object App {
trait A { type L >: Any}
def upcast(erased a: A)(x: Any): a.L = x
erased val p: A { type L <: Nothing } = p
def coerce(x: Any): Int = upcast(p)(x) // error

def coerceInline(x: Any): Int = upcast(compiletime.erasedValue[A {type L <: Nothing}])(x) // error

trait B { type L <: Nothing }
def upcast_dep_parameter(erased a: B)(x: a.L) : Int = x
erased val q : B { type L >: Any } = compiletime.erasedValue
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is no error emitted on this line? What happens if I try to use q's bounds directly on the line below, without passing it as an argument to any function?

I feel like realizability should be checked whenever we try to create an erased value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Realizability checks are always performed when bounds are used, so using q's bounds directly will CE even without this PR.
This PR only adds the checks at function calls (they were performed at definition before, but they are not sufficient per the example).

I had a chat with @odersky about whether checks should be done when we create erased values. We concluded that it's okay to leave it at that to maximize flexibility of erased values.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah. Then everything looks good to me.


def coerceInlineWithB(x: Any): Int = upcast_dep_parameter(q)(x) // error

def main(args: Array[String]): Unit = {
println(coerce("Uh oh!"))
println(coerceInlineWithB("Uh oh!"))
}
}
22 changes: 0 additions & 22 deletions tests/neg-custom-args/i4060.scala

This file was deleted.