Skip to content

Commit b50a29a

Browse files
authored
Ensure erased vals pass CheckRealizable (#16111)
Fix #4060. Per @odersky's [comment](#4060 (comment)), we wish to add realizability checks to all erased vals. - [x] Add realization check to erased definitions typer - [x] Add note about realizibility in the spec - [x] Add tests for #4060
2 parents a70cb46 + e770df3 commit b50a29a

File tree

4 files changed

+30
-23
lines changed

4 files changed

+30
-23
lines changed

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
302302
checkNoConstructorProxy(tree)
303303
transformSelect(tree, Nil)
304304
case tree: Apply =>
305-
val methType = tree.fun.tpe.widen
305+
val methType = tree.fun.tpe.widen.asInstanceOf[MethodType]
306306
val app =
307307
if (methType.isErasedMethod)
308308
tpd.cpy.Apply(tree)(
309309
tree.fun,
310310
tree.args.mapConserve(arg =>
311+
if methType.isResultDependent then
312+
Checking.checkRealizable(arg.tpe, arg.srcPos, "erased argument")
311313
if (methType.isImplicitMethod && arg.span.isSynthetic)
312314
arg match
313315
case _: RefTree | _: Apply | _: TypeApply if arg.symbol.is(Erased) =>

docs/_docs/reference/experimental/erased-defs-spec.md

+6
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,9 @@ TODO: complete
6262
7. Overriding
6363
* Member definitions overriding each other must both be `erased` or not be `erased`
6464
* `def foo(x: T): U` cannot be overridden by `def foo(erased x: T): U` and vice-versa
65+
*
66+
67+
68+
8. Type Restrictions
69+
* For dependent functions, `erased` parameters are limited to realizable types, that is, types that are inhabited by non-null values.
70+
This restriction stops us from using a bad bound introduced by an erased value, which leads to unsoundness (see #4060).
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// See https://github.com/lampepfl/dotty/issues/4060#issuecomment-445808377
2+
3+
object App {
4+
trait A { type L >: Any}
5+
def upcast(erased a: A)(x: Any): a.L = x
6+
erased val p: A { type L <: Nothing } = p
7+
def coerce(x: Any): Int = upcast(p)(x) // error
8+
9+
def coerceInline(x: Any): Int = upcast(compiletime.erasedValue[A {type L <: Nothing}])(x) // error
10+
11+
trait B { type L <: Nothing }
12+
def upcast_dep_parameter(erased a: B)(x: a.L) : Int = x
13+
erased val q : B { type L >: Any } = compiletime.erasedValue
14+
15+
def coerceInlineWithB(x: Any): Int = upcast_dep_parameter(q)(x) // error
16+
17+
def main(args: Array[String]): Unit = {
18+
println(coerce("Uh oh!"))
19+
println(coerceInlineWithB("Uh oh!"))
20+
}
21+
}

tests/neg-custom-args/i4060.scala

-22
This file was deleted.

0 commit comments

Comments
 (0)