Skip to content

Commit 69b9fcf

Browse files
committed
Move postProcessByNameArgs to Nullables
1 parent ae96301 commit 69b9fcf

File tree

2 files changed

+70
-69
lines changed

2 files changed

+70
-69
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 2 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import NameKinds.DefaultGetterName
2222
import ProtoTypes._
2323
import Inferencing._
2424
import transform.TypeUtils._
25-
import Nullables.given
25+
import Nullables.{postProcessByNameArgs, given}
2626

2727
import collection.mutable
28-
import config.Printers.{overload, typr, unapp, nullables}
28+
import config.Printers.{overload, typr, unapp}
2929
import TypeApplications._
3030

3131
import reporting.diagnostic.Message
@@ -1047,73 +1047,6 @@ trait Applications extends Compatibility {
10471047
tree
10481048
}
10491049

1050-
/** Post process all arguments to by-name parameters by removing any not-null
1051-
* info that was used when typing them. Concretely:
1052-
* If an argument corresponds to a call-by-name parameter, drop all
1053-
* embedded not-null assertions of the form `x.$asInstanceOf[x.type & T]`
1054-
* where `x` is a reference to a mutable variable. If the argument still typechecks
1055-
* with the removed assertions and is still compatible with the formal parameter,
1056-
* keep it. Otherwise issue an error that the call-by-name argument was typed using
1057-
* flow assumptions about mutable variables and suggest that it is enclosed
1058-
* in a `byName(...)` call instead.
1059-
*/
1060-
private def postProcessByNameArgs(fn: TermRef, app: Tree)(given ctx: Context): Tree =
1061-
fn.widen match
1062-
case mt: MethodType if mt.paramInfos.exists(_.isInstanceOf[ExprType]) =>
1063-
app match
1064-
case Apply(fn, args) =>
1065-
val dropNotNull = new TreeMap with
1066-
override def transform(t: Tree)(given Context) = t match
1067-
case AssertNotNull(t0) if t0.symbol.is(Mutable) =>
1068-
nullables.println(i"dropping $t")
1069-
transform(t0)
1070-
case t: ValDef if !t.symbol.is(Lazy) => super.transform(t)
1071-
case t: MemberDef =>
1072-
// stop here since embedded references to mutable variables would be
1073-
// out of order, so they would not asserted ot be not-null anyway.
1074-
// @see Nullables.usedOutOfOrder
1075-
t
1076-
case _ => super.transform(t)
1077-
1078-
object retyper extends ReTyper with
1079-
override def typedUnadapted(t: untpd.Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree = t match
1080-
case t: ValDef if !t.symbol.is(Lazy) => super.typedUnadapted(t, pt, locked)
1081-
case t: MemberDef => promote(t)
1082-
case _ => super.typedUnadapted(t, pt, locked)
1083-
1084-
def postProcess(formal: Type, arg: Tree): Tree =
1085-
val arg1 = dropNotNull.transform(arg)
1086-
if arg1 eq arg then arg
1087-
else
1088-
val nestedCtx = ctx.fresh.setNewTyperState()
1089-
val arg2 = retyper.typed(arg1, formal)(given nestedCtx)
1090-
if nestedCtx.reporter.hasErrors || !(arg2.tpe <:< formal) then
1091-
ctx.error(em"""This argument was typed using flow assumptions about mutable variables
1092-
|but it is passed to a by-name parameter where such flow assumptions are unsound.
1093-
|Wrapping the argument in `byName(...)` fixes the problem by disabling the flow assumptions.
1094-
|
1095-
|`byName` needs to be imported from the `scala.compiletime` package.""",
1096-
arg.sourcePos)
1097-
arg
1098-
else
1099-
nestedCtx.typerState.commit()
1100-
arg2
1101-
1102-
def recur(formals: List[Type], args: List[Tree]): List[Tree] = (formals, args) match
1103-
case (formal :: formalsRest, arg :: argsRest) =>
1104-
val arg1 = postProcess(formal.widenExpr.repeatedToSingle, arg)
1105-
val argsRest1 = recur(
1106-
if formal.isRepeatedParam then formals else formalsRest,
1107-
argsRest)
1108-
if (arg1 eq arg) && (argsRest1 eq argsRest) then args
1109-
else arg1 :: argsRest1
1110-
case _ => args
1111-
1112-
tpd.cpy.Apply(app)(fn, recur(mt.paramInfos, args))
1113-
case _ => app
1114-
case _ => app
1115-
end postProcessByNameArgs
1116-
11171050
def typedUnApply(tree: untpd.Apply, selType: Type)(implicit ctx: Context): Tree = {
11181051
record("typedUnApply")
11191052
val Apply(qual, args) = tree

compiler/src/dotty/tools/dotc/typer/Nullables.scala

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import util.Spans.Span
1212
import Flags._
1313
import NullOpsDecorator._
1414
import collection.mutable
15+
import config.Printers.nullables
16+
import ast.{tpd, untpd}
1517

1618
/** Operations for implementing a flow analysis for nullability */
1719
object Nullables with
@@ -450,4 +452,70 @@ object Nullables with
450452
val retractedVars = curCtx.notNullInfos.flatMap(_.asserted.filter(isRetracted)).toSet
451453
curCtx.addNotNullInfo(NotNullInfo(Set(), retractedVars))
452454

455+
/** Post process all arguments to by-name parameters by removing any not-null
456+
* info that was used when typing them. Concretely:
457+
* If an argument corresponds to a call-by-name parameter, drop all
458+
* embedded not-null assertions of the form `x.$asInstanceOf[x.type & T]`
459+
* where `x` is a reference to a mutable variable. If the argument still typechecks
460+
* with the removed assertions and is still compatible with the formal parameter,
461+
* keep it. Otherwise issue an error that the call-by-name argument was typed using
462+
* flow assumptions about mutable variables and suggest that it is enclosed
463+
* in a `byName(...)` call instead.
464+
*/
465+
def postProcessByNameArgs(fn: TermRef, app: Tree)(given ctx: Context): Tree =
466+
fn.widen match
467+
case mt: MethodType if mt.paramInfos.exists(_.isInstanceOf[ExprType]) =>
468+
app match
469+
case Apply(fn, args) =>
470+
val dropNotNull = new TreeMap with
471+
override def transform(t: Tree)(given Context) = t match
472+
case AssertNotNull(t0) if t0.symbol.is(Mutable) =>
473+
nullables.println(i"dropping $t")
474+
transform(t0)
475+
case t: ValDef if !t.symbol.is(Lazy) => super.transform(t)
476+
case t: MemberDef =>
477+
// stop here since embedded references to mutable variables would be
478+
// out of order, so they would not asserted ot be not-null anyway.
479+
// @see Nullables.usedOutOfOrder
480+
t
481+
case _ => super.transform(t)
482+
483+
object retyper extends ReTyper with
484+
override def typedUnadapted(t: untpd.Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree = t match
485+
case t: ValDef if !t.symbol.is(Lazy) => super.typedUnadapted(t, pt, locked)
486+
case t: MemberDef => promote(t)
487+
case _ => super.typedUnadapted(t, pt, locked)
488+
489+
def postProcess(formal: Type, arg: Tree): Tree =
490+
val arg1 = dropNotNull.transform(arg)
491+
if arg1 eq arg then arg
492+
else
493+
val nestedCtx = ctx.fresh.setNewTyperState()
494+
val arg2 = retyper.typed(arg1, formal)(given nestedCtx)
495+
if nestedCtx.reporter.hasErrors || !(arg2.tpe <:< formal) then
496+
ctx.error(em"""This argument was typed using flow assumptions about mutable variables
497+
|but it is passed to a by-name parameter where such flow assumptions are unsound.
498+
|Wrapping the argument in `byName(...)` fixes the problem by disabling the flow assumptions.
499+
|
500+
|`byName` needs to be imported from the `scala.compiletime` package.""",
501+
arg.sourcePos)
502+
arg
503+
else
504+
nestedCtx.typerState.commit()
505+
arg2
506+
507+
def recur(formals: List[Type], args: List[Tree]): List[Tree] = (formals, args) match
508+
case (formal :: formalsRest, arg :: argsRest) =>
509+
val arg1 = postProcess(formal.widenExpr.repeatedToSingle, arg)
510+
val argsRest1 = recur(
511+
if formal.isRepeatedParam then formals else formalsRest,
512+
argsRest)
513+
if (arg1 eq arg) && (argsRest1 eq argsRest) then args
514+
else arg1 :: argsRest1
515+
case _ => args
516+
517+
tpd.cpy.Apply(app)(fn, recur(mt.paramInfos, args))
518+
case _ => app
519+
case _ => app
520+
end postProcessByNameArgs
453521
end Nullables

0 commit comments

Comments
 (0)