@@ -10,14 +10,16 @@ import config.Printers.{capt, recheckr}
10
10
import config .{Config , Feature }
11
11
import ast .{tpd , untpd , Trees }
12
12
import Trees .*
13
- import typer .RefChecks .{checkAllOverrides , checkParents }
13
+ import typer .RefChecks .{checkAllOverrides , checkSelfAgainstParents }
14
+ import typer .Checking .{checkBounds , checkAppliedTypesIn }
14
15
import util .{SimpleIdentitySet , EqHashMap , SrcPos }
15
16
import transform .SymUtils .*
16
17
import transform .{Recheck , PreRecheck }
17
18
import Recheck .*
18
19
import scala .collection .mutable
19
20
import CaptureSet .{withCaptureSetsExplained , IdempotentCaptRefMap }
20
21
import StdNames .nme
22
+ import NameKinds .DefaultGetterName
21
23
import reporting .trace
22
24
23
25
/** The capture checker */
@@ -335,12 +337,21 @@ class CheckCaptures extends Recheck, SymTransformer:
335
337
override def recheckApply (tree : Apply , pt : Type )(using Context ): Type =
336
338
val meth = tree.fun.symbol
337
339
includeCallCaptures(meth, tree.srcPos)
338
- if meth == defn. Caps_unsafeBox || meth == defn. Caps_unsafeUnbox then
340
+ def mapArgUsing ( f : Type => Type ) =
339
341
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))
342
343
val argType = super .recheckFinish(argType0, arg, pt)
343
344
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
+ }
344
355
else
345
356
super .recheckApply(tree, pt) match
346
357
case appType @ CapturingType (appType1, refs) =>
@@ -432,7 +443,8 @@ class CheckCaptures extends Recheck, SymTransformer:
432
443
block match
433
444
case closureDef(mdef) =>
434
445
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) =>
436
448
// Redo setup of the anonymous function so that formal parameters don't
437
449
// get capture sets. This is important to avoid false widenings to `*`
438
450
// when taking the base type of the actual closures's dependent function
@@ -442,46 +454,32 @@ class CheckCaptures extends Recheck, SymTransformer:
442
454
// First, undo the previous setup which installed a completer for `meth`.
443
455
atPhase(preRecheckPhase.prev)(meth.denot.copySymDenotation())
444
456
.installAfter(preRecheckPhase)
457
+
445
458
// Next, update all parameter symbols to match expected formals
446
459
meth.paramSymss.head.lazyZip(ptformals).foreach { (psym, pformal) =>
447
- psym.copySymDenotation(info = pformal).installAfter(preRecheckPhase )
460
+ psym.updateInfoBetween(preRecheckPhase, thisPhase, pformal.mapExprType )
448
461
}
449
462
// Next, update types of parameter ValDefs
450
463
mdef.paramss.head.lazyZip(ptformals).foreach { (param, pformal) =>
451
464
val ValDef (_, tpt, _) = param : @ unchecked
452
465
tpt.rememberTypeAlways(pformal)
453
466
}
454
467
// Next, install a new completer reflecting the new parameters for the anonymous method
468
+ val mt = meth.info.asInstanceOf [MethodType ]
455
469
val completer = new LazyType :
456
470
def complete (denot : SymDenotation )(using Context ) =
457
- denot.info = MethodType (ptformals, mdef.tpt.knownType)
471
+ denot.info = mt.companion (ptformals, mdef.tpt.knownType)
458
472
.showing(i " simplify info of $meth to $result" , capt)
459
473
recheckDef(mdef, meth)
460
- meth.copySymDenotation(info = completer, initFlags = meth.flags &~ Touched )
461
- .installAfter(preRecheckPhase)
474
+ meth.updateInfoBetween(preRecheckPhase, thisPhase, completer)
462
475
case _ =>
463
476
case _ =>
464
477
super .recheckBlock(block, pt)
465
478
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
-
480
479
override def recheckValDef (tree : ValDef , sym : Symbol )(using Context ): Unit =
481
480
try
482
481
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)
485
483
finally
486
484
if ! sym.is(Param ) then
487
485
// Parameters with inferred types belong to anonymous methods. We need to wait
@@ -503,7 +501,8 @@ class CheckCaptures extends Recheck, SymTransformer:
503
501
/** Class-specific capture set relations:
504
502
* 1. The capture set of a class includes the capture sets of its parents.
505
503
* 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.
507
506
*/
508
507
override def recheckClassDef (tree : TypeDef , impl : Template , cls : ClassSymbol )(using Context ): Type =
509
508
val saved = curEnv
@@ -515,7 +514,12 @@ class CheckCaptures extends Recheck, SymTransformer:
515
514
val thisSet = cls.classInfo.selfType.captureSet.withDescription(i " of the self type of $cls" )
516
515
checkSubset(localSet, thisSet, tree.srcPos) // (2)
517
516
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)
519
523
super .recheckClassDef(tree, impl, cls)
520
524
finally
521
525
curEnv = saved
@@ -772,7 +776,8 @@ class CheckCaptures extends Recheck, SymTransformer:
772
776
// We can't box/unbox the universal capability. Leave `actual` as it is
773
777
// so we get an error in checkConforms. This tends to give better error
774
778
// 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" )
776
781
actual
777
782
else
778
783
// Disallow future addition of `*` to `criticalSet`.
@@ -845,13 +850,21 @@ class CheckCaptures extends Recheck, SymTransformer:
845
850
cls => ! parentTrees(cls).exists(ptree => parentTrees.contains(ptree.tpe.classSymbol))
846
851
}
847
852
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 )
850
855
val selfType = root.asClass.classInfo.selfType
851
856
interpolator(startingVariance = - 1 ).traverse(selfType)
852
857
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
+ }
853
863
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.
855
868
report.error(
856
869
i """ $root needs an explicitly declared self type since its
857
870
|inferred self type $selfType
@@ -867,6 +880,7 @@ class CheckCaptures extends Recheck, SymTransformer:
867
880
* - Check that externally visible `val`s or `def`s have empty capture sets. If not,
868
881
* suggest an explicit type. This is so that separate compilation (where external
869
882
* symbols have empty capture sets) gives the same results as joint compilation.
883
+ * - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
870
884
*/
871
885
def postCheck (unit : tpd.Tree )(using Context ): Unit =
872
886
unit.foreachSubTree {
@@ -885,15 +899,23 @@ class CheckCaptures extends Recheck, SymTransformer:
885
899
val isLocal =
886
900
sym.owner.ownersIterator.exists(_.isTerm)
887
901
|| 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
894
915
val inferred = t.tpt.knownType
895
916
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 =>
897
919
val resultStr = if t.isInstanceOf [DefDef ] then " result" else " "
898
920
report.error(
899
921
em """ Non-local $sym cannot have an inferred $resultStr type
@@ -902,8 +924,27 @@ class CheckCaptures extends Recheck, SymTransformer:
902
924
|The type needs to be declared explicitly. """ , t.srcPos)
903
925
case _ =>
904
926
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 _ =>
905
936
case _ =>
906
937
}
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)
908
949
end CaptureChecker
909
950
end CheckCaptures
0 commit comments