@@ -10,14 +10,16 @@ import config.Printers.{capt, recheckr}
1010import config .{Config , Feature }
1111import ast .{tpd , untpd , Trees }
1212import Trees .*
13- import typer .RefChecks .{checkAllOverrides , checkParents }
13+ import typer .RefChecks .{checkAllOverrides , checkSelfAgainstParents }
14+ import typer .Checking .{checkBounds , checkAppliedTypesIn }
1415import util .{SimpleIdentitySet , EqHashMap , SrcPos }
1516import transform .SymUtils .*
1617import transform .{Recheck , PreRecheck }
1718import Recheck .*
1819import scala .collection .mutable
1920import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap }
2021import StdNames .nme
22+ import NameKinds .DefaultGetterName
2123import reporting .trace
2224
2325/** The capture checker */
@@ -335,12 +337,21 @@ class CheckCaptures extends Recheck, SymTransformer:
335337 override def recheckApply (tree : Apply , pt : Type )(using Context ): Type =
336338 val meth = tree.fun.symbol
337339 includeCallCaptures(meth, tree.srcPos)
338- if meth == defn. Caps_unsafeBox || meth == defn. Caps_unsafeUnbox then
340+ def mapArgUsing ( f : Type => Type ) =
339341 val arg :: Nil = tree.args: @ unchecked
340- val argType0 = recheckStart(arg, pt)
341- .forceBoxStatus(boxed = meth == defn.Caps_unsafeBox )
342+ val argType0 = f(recheckStart(arg, pt))
342343 val argType = super .recheckFinish(argType0, arg, pt)
343344 super .recheckFinish(argType, tree, pt)
345+
346+ if meth == defn.Caps_unsafeBox then
347+ mapArgUsing(_.forceBoxStatus(true ))
348+ else if meth == defn.Caps_unsafeUnbox then
349+ mapArgUsing(_.forceBoxStatus(false ))
350+ else if meth == defn.Caps_unsafeBoxFunArg then
351+ mapArgUsing {
352+ case defn.FunctionOf (paramtpe :: Nil , restpe, isContectual, isErased) =>
353+ defn.FunctionOf (paramtpe.forceBoxStatus(true ) :: Nil , restpe, isContectual, isErased)
354+ }
344355 else
345356 super .recheckApply(tree, pt) match
346357 case appType @ CapturingType (appType1, refs) =>
@@ -432,7 +443,8 @@ class CheckCaptures extends Recheck, SymTransformer:
432443 block match
433444 case closureDef(mdef) =>
434445 pt.dealias match
435- case defn.FunctionOf (ptformals, _, _, _) if ptformals.forall(_.captureSet.isAlwaysEmpty) =>
446+ case defn.FunctionOf (ptformals, _, _, _)
447+ if ptformals.nonEmpty && ptformals.forall(_.captureSet.isAlwaysEmpty) =>
436448 // Redo setup of the anonymous function so that formal parameters don't
437449 // get capture sets. This is important to avoid false widenings to `*`
438450 // when taking the base type of the actual closures's dependent function
@@ -442,46 +454,32 @@ class CheckCaptures extends Recheck, SymTransformer:
442454 // First, undo the previous setup which installed a completer for `meth`.
443455 atPhase(preRecheckPhase.prev)(meth.denot.copySymDenotation())
444456 .installAfter(preRecheckPhase)
457+
445458 // Next, update all parameter symbols to match expected formals
446459 meth.paramSymss.head.lazyZip(ptformals).foreach { (psym, pformal) =>
447- psym.copySymDenotation(info = pformal).installAfter(preRecheckPhase )
460+ psym.updateInfoBetween(preRecheckPhase, thisPhase, pformal.mapExprType )
448461 }
449462 // Next, update types of parameter ValDefs
450463 mdef.paramss.head.lazyZip(ptformals).foreach { (param, pformal) =>
451464 val ValDef (_, tpt, _) = param : @ unchecked
452465 tpt.rememberTypeAlways(pformal)
453466 }
454467 // Next, install a new completer reflecting the new parameters for the anonymous method
468+ val mt = meth.info.asInstanceOf [MethodType ]
455469 val completer = new LazyType :
456470 def complete (denot : SymDenotation )(using Context ) =
457- denot.info = MethodType (ptformals, mdef.tpt.knownType)
471+ denot.info = mt.companion (ptformals, mdef.tpt.knownType)
458472 .showing(i " simplify info of $meth to $result" , capt)
459473 recheckDef(mdef, meth)
460- meth.copySymDenotation(info = completer, initFlags = meth.flags &~ Touched )
461- .installAfter(preRecheckPhase)
474+ meth.updateInfoBetween(preRecheckPhase, thisPhase, completer)
462475 case _ =>
463476 case _ =>
464477 super .recheckBlock(block, pt)
465478
466- /** If `rhsProto` has `*` as its capture set, wrap `rhs` in a `unsafeBox`.
467- * Used to infer `unsafeBox` for expressions that get assigned to variables
468- * that have universal capture set.
469- */
470- def maybeBox (rhs : Tree , rhsProto : Type )(using Context ): Tree =
471- if rhsProto.captureSet.isUniversal then
472- ref(defn.Caps_unsafeBox ).appliedToType(rhsProto).appliedTo(rhs)
473- else rhs
474-
475- override def recheckAssign (tree : Assign )(using Context ): Type =
476- val rhsProto = recheck(tree.lhs).widen
477- recheck(maybeBox(tree.rhs, rhsProto), rhsProto)
478- defn.UnitType
479-
480479 override def recheckValDef (tree : ValDef , sym : Symbol )(using Context ): Unit =
481480 try
482481 if ! sym.is(Module ) then // Modules are checked by checking the module class
483- if sym.is(Mutable ) then recheck(maybeBox(tree.rhs, sym.info), sym.info)
484- else super .recheckValDef(tree, sym)
482+ super .recheckValDef(tree, sym)
485483 finally
486484 if ! sym.is(Param ) then
487485 // Parameters with inferred types belong to anonymous methods. We need to wait
@@ -503,7 +501,8 @@ class CheckCaptures extends Recheck, SymTransformer:
503501 /** Class-specific capture set relations:
504502 * 1. The capture set of a class includes the capture sets of its parents.
505503 * 2. The capture set of the self type of a class includes the capture set of the class.
506- * 3. The capture set of the self type of a class includes the capture set of every class parameter.
504+ * 3. The capture set of the self type of a class includes the capture set of every class parameter,
505+ * unless the parameter is marked @constructorOnly.
507506 */
508507 override def recheckClassDef (tree : TypeDef , impl : Template , cls : ClassSymbol )(using Context ): Type =
509508 val saved = curEnv
@@ -515,7 +514,12 @@ class CheckCaptures extends Recheck, SymTransformer:
515514 val thisSet = cls.classInfo.selfType.captureSet.withDescription(i " of the self type of $cls" )
516515 checkSubset(localSet, thisSet, tree.srcPos) // (2)
517516 for param <- cls.paramGetters do
518- checkSubset(param.termRef.captureSet, thisSet, param.srcPos) // (3)
517+ if ! param.hasAnnotation(defn.ConstructorOnlyAnnot ) then
518+ checkSubset(param.termRef.captureSet, thisSet, param.srcPos) // (3)
519+ for pureBase <- cls.pureBaseClass do
520+ checkSubset(thisSet,
521+ CaptureSet .empty.withDescription(i " of pure base class $pureBase" ),
522+ tree.srcPos)
519523 super .recheckClassDef(tree, impl, cls)
520524 finally
521525 curEnv = saved
@@ -772,7 +776,8 @@ class CheckCaptures extends Recheck, SymTransformer:
772776 // We can't box/unbox the universal capability. Leave `actual` as it is
773777 // so we get an error in checkConforms. This tends to give better error
774778 // messages than disallowing the root capability in `criticalSet`.
775- capt.println(i " cannot box/unbox $actual vs $expected" )
779+ if ctx.settings.YccDebug .value then
780+ println(i " cannot box/unbox $actual vs $expected" )
776781 actual
777782 else
778783 // Disallow future addition of `*` to `criticalSet`.
@@ -845,13 +850,21 @@ class CheckCaptures extends Recheck, SymTransformer:
845850 cls => ! parentTrees(cls).exists(ptree => parentTrees.contains(ptree.tpe.classSymbol))
846851 }
847852 assert(roots.nonEmpty)
848- for root <- roots do
849- checkParents (root, parentTrees( root) )
853+ for case root : ClassSymbol <- roots do
854+ checkSelfAgainstParents (root, root.baseClasses )
850855 val selfType = root.asClass.classInfo.selfType
851856 interpolator(startingVariance = - 1 ).traverse(selfType)
852857 if ! root.isEffectivelySealed then
858+ def matchesExplicitRefsInBaseClass (refs : CaptureSet , cls : ClassSymbol ): Boolean =
859+ cls.baseClasses.tail.exists { psym =>
860+ val selfType = psym.asClass.givenSelfType
861+ selfType.exists && selfType.captureSet.elems == refs.elems
862+ }
853863 selfType match
854- case CapturingType (_, refs : CaptureSet .Var ) if ! refs.isUniversal =>
864+ case CapturingType (_, refs : CaptureSet .Var )
865+ if ! refs.isUniversal && ! matchesExplicitRefsInBaseClass(refs, root) =>
866+ // Forbid inferred self types unless they are already implied by an explicit
867+ // self type in a parent.
855868 report.error(
856869 i """ $root needs an explicitly declared self type since its
857870 |inferred self type $selfType
@@ -867,6 +880,7 @@ class CheckCaptures extends Recheck, SymTransformer:
867880 * - Check that externally visible `val`s or `def`s have empty capture sets. If not,
868881 * suggest an explicit type. This is so that separate compilation (where external
869882 * symbols have empty capture sets) gives the same results as joint compilation.
883+ * - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
870884 */
871885 def postCheck (unit : tpd.Tree )(using Context ): Unit =
872886 unit.foreachSubTree {
@@ -885,15 +899,23 @@ class CheckCaptures extends Recheck, SymTransformer:
885899 val isLocal =
886900 sym.owner.ownersIterator.exists(_.isTerm)
887901 || sym.accessBoundary(defn.RootClass ).isContainedIn(sym.topLevelClass)
888-
889- // The following classes of definitions need explicit capture types ...
890- if ! isLocal // ... since external capture types are not inferred
891- || sym.owner.is(Trait ) // ... since we do OverridingPairs checking before capture inference
892- || sym.allOverriddenSymbols.nonEmpty // ... since we do override checking before capture inference
893- then
902+ def canUseInferred = // If canUseInferred is false, all capturing types in the type of `sym` need to be given explicitly
903+ sym.is(Private ) // private symbols can always have inferred types
904+ || sym.name.is(DefaultGetterName ) // default getters are exempted since otherwise it would be
905+ // too annoying. This is a hole since a defualt getter's result type
906+ // might leak into a type variable.
907+ || // non-local symbols cannot have inferred types since external capture types are not inferred
908+ isLocal // local symbols still need explicit types if
909+ && ! sym.owner.is(Trait ) // they are defined in a trait, since we do OverridingPairs checking before capture inference
910+ def isNotPureThis (ref : CaptureRef ) = ref match {
911+ case ref : ThisType => ! ref.cls.isPureClass
912+ case _ => true
913+ }
914+ if ! canUseInferred then
894915 val inferred = t.tpt.knownType
895916 def checkPure (tp : Type ) = tp match
896- case CapturingType (_, refs) if ! refs.elems.isEmpty =>
917+ case CapturingType (_, refs)
918+ if ! refs.elems.filter(isNotPureThis).isEmpty =>
897919 val resultStr = if t.isInstanceOf [DefDef ] then " result" else " "
898920 report.error(
899921 em """ Non-local $sym cannot have an inferred $resultStr type
@@ -902,8 +924,27 @@ class CheckCaptures extends Recheck, SymTransformer:
902924 |The type needs to be declared explicitly. """ , t.srcPos)
903925 case _ =>
904926 inferred.foreachPart(checkPure, StopAt .Static )
927+ case t @ TypeApply (fun, args) =>
928+ fun.knownType.widen match
929+ case tl : PolyType =>
930+ val normArgs = args.lazyZip(tl.paramInfos).map { (arg, bounds) =>
931+ arg.withType(arg.knownType.forceBoxStatus(
932+ bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing))
933+ }
934+ checkBounds(normArgs, tl)
935+ case _ =>
905936 case _ =>
906937 }
907-
938+ if ! ctx.reporter.errorsReported then
939+ // We dont report errors here if previous errors were reported, because other
940+ // errors often result in bad applied types, but flagging these bad types gives
941+ // often worse error messages than the original errors.
942+ val checkApplied = new TreeTraverser :
943+ def traverse (t : Tree )(using Context ) = t match
944+ case tree : InferredTypeTree =>
945+ case tree : New =>
946+ case tree : TypeTree => checkAppliedTypesIn(tree.withKnownType)
947+ case _ => traverseChildren(t)
948+ checkApplied.traverse(unit)
908949 end CaptureChecker
909950end CheckCaptures
0 commit comments