Skip to content

Commit b429e6b

Browse files
committed
Check realizability of parameters of an erased function
... instead of checking every instantiated instance of erasedValue[T]. I think this is the better solution, since erasedValue have uses outside of erasedDefinitions (i.e. in inlining) which has their own realizability checks as well. We don't have to be pessimistic in those cases. - Only check for realizability for erased parameters of result-dependent functions. Per talk with @odersky, it seems that it is enough to check for realizability of parameters of a dependent function in order to avoid unsoundness. Note that the following actions have already been checked for, with erased parameters: - Using them as a dependent path in casting / variable definitions - Using them as the input to an `inline match` The check previously performed on `val`/`def` definitions are also removed, so we are as liberal as possible, to avoid too much restriction. I think with this, we can also remove the special case for `compiletime.erasedValue`.
1 parent f18465b commit b429e6b

File tree

2 files changed

+4
-10
lines changed

2 files changed

+4
-10
lines changed

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

+3-9
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) =>
@@ -350,11 +352,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
350352
if (fn.symbol != defn.ChildAnnot.primaryConstructor)
351353
// Make an exception for ChildAnnot, which should really have AnyKind bounds
352354
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
353-
if fn.symbol eq defn.Compiletime_erasedValue then
354-
// Check the instantiated type of erasedValue for realizibility
355-
for arg <- args do
356-
Checking.checkRealizable(arg.tpe, arg.srcPos, "type application of erasedValue")
357-
358355
fn match {
359356
case sel: Select =>
360357
val args1 = transform(args)
@@ -489,9 +486,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
489486
private def checkErasedDef(tree: ValOrDefDef)(using Context): Unit =
490487
if tree.symbol.is(Erased, butNot = Macro) then
491488
val tpe = tree.rhs.tpe
492-
if !tree.symbol.isOneOf(TermParamOrAccessor) && !tree.symbol.eq(defn.Compiletime_erasedValue) then // Only need to check non-parameters, since parameters have their own path checks.
493-
// We want all erased definitions to have a realizable type
494-
Checking.checkRealizable(tree.tpt.tpe, tree.srcPos, "erased type")
495489
if tpe.derivesFrom(defn.NothingClass) then
496490
report.error("`erased` definition cannot be implemented with en expression of type Nothing", tree.srcPos)
497491
else if tpe.derivesFrom(defn.NullClass) then

tests/neg-custom-args/erased/i4060.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ object App {
77
def upcast(erased a: A)(x: Any): a.L = x
88
//lazy val p: A { type L <: Nothing } = p
99
erased val p: A { type L <: Nothing } = p // error
10-
def coerce(x: Any): Int = upcast(p)(x)
10+
def coerce(x: Any): Int = upcast(p)(x) // error
1111

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

0 commit comments

Comments
 (0)