@@ -12,6 +12,8 @@ import util.Spans.Span
1212import Flags ._
1313import NullOpsDecorator ._
1414import collection .mutable
15+ import config .Printers .nullables
16+ import ast .{tpd , untpd }
1517
1618/** Operations for implementing a flow analysis for nullability */
1719object Nullables with
@@ -182,6 +184,13 @@ object Nullables with
182184 then infos
183185 else info :: infos
184186
187+ /** Retract all references to mutable variables */
188+ def retractMutables (given Context ) =
189+ val mutables = infos.foldLeft(Set [TermRef ]())((ms, info) =>
190+ ms.union(info.asserted.filter(_.symbol.is(Mutable ))))
191+ infos.extendWith(NotNullInfo (Set (), mutables))
192+ end notNullInfoOps
193+
185194 given refOps : extension (ref : TermRef ) with
186195
187196 /** Is the use of a mutable variable out of order
@@ -443,4 +452,70 @@ object Nullables with
443452 val retractedVars = curCtx.notNullInfos.flatMap(_.asserted.filter(isRetracted)).toSet
444453 curCtx.addNotNullInfo(NotNullInfo (Set (), retractedVars))
445454
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
446521end Nullables
0 commit comments