Skip to content

Commit ef97ee2

Browse files
authored
More additions to the standard library (#18799)
... and fixes to make that happen.
2 parents 5454110 + 5e49b12 commit ef97ee2

File tree

150 files changed

+35907
-189
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+35907
-189
lines changed

compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ class TreeTypeMap(
105105
tree1.withType(mapType(tree1.tpe)) match {
106106
case id: Ident =>
107107
if needsSelect(id.tpe) then
108-
ref(id.tpe.asInstanceOf[TermRef]).withSpan(id.span)
108+
try ref(id.tpe.asInstanceOf[TermRef]).withSpan(id.span)
109+
catch case ex: TypeError => super.transform(id)
109110
else
110111
super.transform(id)
111112
case sel: Select =>

compiler/src/dotty/tools/dotc/cc/CaptureOps.scala

+11-1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,12 @@ extension (tp: Type)
206206
case _: TypeRef | _: AppliedType => tp.typeSymbol.hasAnnotation(defn.CapabilityAnnot)
207207
case _ => false
208208

209+
def isSealed(using Context): Boolean = tp match
210+
case tp: TypeParamRef => tp.underlying.isSealed
211+
case tp: TypeBounds => tp.hi.hasAnnotation(defn.Caps_SealedAnnot)
212+
case tp: TypeRef => tp.symbol.is(Sealed) || tp.info.isSealed // TODO: drop symbol flag?
213+
case _ => false
214+
209215
/** Drop @retains annotations everywhere */
210216
def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling
211217
val tm = new TypeMap:
@@ -225,7 +231,11 @@ extension (cls: ClassSymbol)
225231
&& bc.givenSelfType.dealiasKeepAnnots.match
226232
case CapturingType(_, refs) => refs.isAlwaysEmpty
227233
case RetainingType(_, refs) => refs.isEmpty
228-
case selfType => selfType.exists && selfType.captureSet.isAlwaysEmpty
234+
case selfType =>
235+
isCaptureChecking // At Setup we have not processed self types yet, so
236+
// unless a self type is explicitly given, we can't tell
237+
// and err on the side of impure.
238+
&& selfType.exists && selfType.captureSet.isAlwaysEmpty
229239

230240
extension (sym: Symbol)
231241

compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

+1
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ object CaptureSet:
872872
upper.isAlwaysEmpty || upper.isConst && upper.elems.size == 1 && upper.elems.contains(r1)
873873
if variance > 0 || isExact then upper
874874
else if variance < 0 then CaptureSet.empty
875+
else if ctx.mode.is(Mode.Printing) then upper
875876
else assert(false, i"trying to add $upper from $r via ${tm.getClass} in a non-variant setting")
876877

877878
/** Apply `f` to each element in `xs`, and join result sets with `++` */

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

+148-39
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import typer.RefChecks.{checkAllOverrides, checkSelfAgainstParents, OverridingPa
1414
import typer.Checking.{checkBounds, checkAppliedTypesIn}
1515
import typer.ErrorReporting.{Addenda, err}
1616
import typer.ProtoTypes.{AnySelectionProto, LhsProto}
17-
import util.{SimpleIdentitySet, EqHashMap, SrcPos, Property}
17+
import util.{SimpleIdentitySet, EqHashMap, EqHashSet, SrcPos, Property}
1818
import transform.SymUtils.*
19-
import transform.{Recheck, PreRecheck}
19+
import transform.{Recheck, PreRecheck, CapturedVars}
2020
import Recheck.*
2121
import scala.collection.mutable
2222
import CaptureSet.{withCaptureSetsExplained, IdempotentCaptRefMap, CompareResult}
2323
import StdNames.nme
24-
import NameKinds.DefaultGetterName
24+
import NameKinds.{DefaultGetterName, WildcardParamName}
2525
import reporting.trace
2626

2727
/** The capture checker */
@@ -147,33 +147,49 @@ object CheckCaptures:
147147
private def disallowRootCapabilitiesIn(tp: Type, carrier: Symbol, what: String, have: String, addendum: String, pos: SrcPos)(using Context) =
148148
val check = new TypeTraverser:
149149

150+
private val seen = new EqHashSet[TypeRef]
151+
152+
/** Check that there is at least one method containing carrier and defined
153+
* in the scope of tparam. E.g. this is OK:
154+
* def f[T] = { ... var x: T ... }
155+
* So is this:
156+
* class C[T] { def f() = { class D { var x: T }}}
157+
* But this is not OK:
158+
* class C[T] { object o { var x: T }}
159+
*/
150160
extension (tparam: Symbol) def isParametricIn(carrier: Symbol): Boolean =
151-
val encl = carrier.owner.enclosingMethodOrClass
152-
if encl.isClass then tparam.isParametricIn(encl)
153-
else
154-
def recur(encl: Symbol): Boolean =
155-
if tparam.owner == encl then true
156-
else if encl.isStatic || !encl.exists then false
157-
else recur(encl.owner.enclosingMethodOrClass)
158-
recur(encl)
161+
carrier.exists && {
162+
val encl = carrier.owner.enclosingMethodOrClass
163+
if encl.isClass then tparam.isParametricIn(encl)
164+
else
165+
def recur(encl: Symbol): Boolean =
166+
if tparam.owner == encl then true
167+
else if encl.isStatic || !encl.exists then false
168+
else recur(encl.owner.enclosingMethodOrClass)
169+
recur(encl)
170+
}
159171

160172
def traverse(t: Type) =
161173
t.dealiasKeepAnnots match
162174
case t: TypeRef =>
163-
capt.println(i"disallow $t, $tp, $what, ${t.symbol.is(Sealed)}")
164-
t.info match
165-
case TypeBounds(_, hi)
166-
if !t.symbol.is(Sealed) && !t.symbol.isParametricIn(carrier) =>
167-
if hi.isAny then
168-
report.error(
169-
em"""$what cannot $have $tp since
170-
|that type refers to the type variable $t, which is not sealed.
171-
|$addendum""",
172-
pos)
173-
else
174-
traverse(hi)
175-
case _ =>
176-
traverseChildren(t)
175+
if !seen.contains(t) then
176+
capt.println(i"disallow $t, $tp, $what, ${t.isSealed}")
177+
seen += t
178+
t.info match
179+
case TypeBounds(_, hi) if !t.isSealed && !t.symbol.isParametricIn(carrier) =>
180+
if hi.isAny then
181+
val detailStr =
182+
if t eq tp then "variable"
183+
else i"refers to the type variable $t, which"
184+
report.error(
185+
em"""$what cannot $have $tp since
186+
|that type $detailStr is not sealed.
187+
|$addendum""",
188+
pos)
189+
else
190+
traverse(hi)
191+
case _ =>
192+
traverseChildren(t)
177193
case AnnotatedType(_, ann) if ann.symbol == defn.UncheckedCapturesAnnot =>
178194
()
179195
case t =>
@@ -260,11 +276,12 @@ class CheckCaptures extends Recheck, SymTransformer:
260276
pos, provenance)
261277

262278
/** Check subcapturing `cs1 <: cs2`, report error on failure */
263-
def checkSubset(cs1: CaptureSet, cs2: CaptureSet, pos: SrcPos, provenance: => String = "")(using Context) =
279+
def checkSubset(cs1: CaptureSet, cs2: CaptureSet, pos: SrcPos,
280+
provenance: => String = "", cs1description: String = "")(using Context) =
264281
checkOK(
265282
cs1.subCaptures(cs2, frozen = false),
266-
if cs1.elems.size == 1 then i"reference ${cs1.elems.toList.head} is not"
267-
else i"references $cs1 are not all",
283+
if cs1.elems.size == 1 then i"reference ${cs1.elems.toList.head}$cs1description is not"
284+
else i"references $cs1$cs1description are not all",
268285
pos, provenance)
269286

270287
/** The current environment */
@@ -542,10 +559,10 @@ class CheckCaptures extends Recheck, SymTransformer:
542559
val TypeApply(fn, args) = tree
543560
val polyType = atPhase(thisPhase.prev):
544561
fn.tpe.widen.asInstanceOf[TypeLambda]
545-
for case (arg: TypeTree, pinfo, pname) <- args.lazyZip(polyType.paramInfos).lazyZip((polyType.paramNames)) do
546-
if pinfo.bounds.hi.hasAnnotation(defn.Caps_SealedAnnot) then
562+
for case (arg: TypeTree, formal, pname) <- args.lazyZip(polyType.paramRefs).lazyZip((polyType.paramNames)) do
563+
if formal.isSealed then
547564
def where = if fn.symbol.exists then i" in an argument of ${fn.symbol}" else ""
548-
disallowRootCapabilitiesIn(arg.knownType, fn.symbol,
565+
disallowRootCapabilitiesIn(arg.knownType, NoSymbol,
549566
i"Sealed type variable $pname", "be instantiated to",
550567
i"This is often caused by a local capability$where\nleaking as part of its result.",
551568
tree.srcPos)
@@ -586,13 +603,58 @@ class CheckCaptures extends Recheck, SymTransformer:
586603
openClosures = openClosures.tail
587604
end recheckClosureBlock
588605

606+
/** Maps mutable variables to the symbols that capture them (in the
607+
* CheckCaptures sense, i.e. symbol is referred to from a different method
608+
* than the one it is defined in).
609+
*/
610+
private val capturedBy = util.HashMap[Symbol, Symbol]()
611+
612+
/** Maps anonymous functions appearing as function arguments to
613+
* the function that is called.
614+
*/
615+
private val anonFunCallee = util.HashMap[Symbol, Symbol]()
616+
617+
/** Populates `capturedBy` and `anonFunCallee`. Called by `checkUnit`.
618+
*/
619+
private def collectCapturedMutVars(using Context) = new TreeTraverser:
620+
def traverse(tree: Tree)(using Context) = tree match
621+
case id: Ident =>
622+
val sym = id.symbol
623+
if sym.is(Mutable, butNot = Method) && sym.owner.isTerm then
624+
val enclMeth = ctx.owner.enclosingMethod
625+
if sym.enclosingMethod != enclMeth then
626+
capturedBy(sym) = enclMeth
627+
case Apply(fn, args) =>
628+
for case closureDef(mdef) <- args do
629+
anonFunCallee(mdef.symbol) = fn.symbol
630+
traverseChildren(tree)
631+
case Inlined(_, bindings, expansion) =>
632+
traverse(bindings)
633+
traverse(expansion)
634+
case mdef: DefDef =>
635+
if !mdef.symbol.isInlineMethod then traverseChildren(tree)
636+
case _ =>
637+
traverseChildren(tree)
638+
589639
override def recheckValDef(tree: ValDef, sym: Symbol)(using Context): Type =
590640
try
591641
if sym.is(Module) then sym.info // Modules are checked by checking the module class
592642
else
593643
if sym.is(Mutable) && !sym.hasAnnotation(defn.UncheckedCapturesAnnot) then
594-
disallowRootCapabilitiesIn(tree.tpt.knownType, sym,
595-
i"mutable $sym", "have type", "", sym.srcPos)
644+
val (carrier, addendum) = capturedBy.get(sym) match
645+
case Some(encl) =>
646+
val enclStr =
647+
if encl.isAnonymousFunction then
648+
val location = anonFunCallee.get(encl) match
649+
case Some(meth) if meth.exists => i" argument in a call to $meth"
650+
case _ => ""
651+
s"an anonymous function$location"
652+
else encl.show
653+
(NoSymbol, i"\nNote that $sym does not count as local since it is captured by $enclStr")
654+
case _ =>
655+
(sym, "")
656+
disallowRootCapabilitiesIn(
657+
tree.tpt.knownType, carrier, i"Mutable $sym", "have type", addendum, sym.srcPos)
596658
checkInferredResult(super.recheckValDef(tree, sym), tree)
597659
finally
598660
if !sym.is(Param) then
@@ -680,9 +742,15 @@ class CheckCaptures extends Recheck, SymTransformer:
680742
if !param.hasAnnotation(defn.ConstructorOnlyAnnot) then
681743
checkSubset(param.termRef.captureSet, thisSet, param.srcPos) // (3)
682744
for pureBase <- cls.pureBaseClass do // (4)
745+
def selfType = impl.body
746+
.collect:
747+
case TypeDef(tpnme.SELF, rhs) => rhs
748+
.headOption
749+
.getOrElse(tree)
750+
.orElse(tree)
683751
checkSubset(thisSet,
684752
CaptureSet.empty.withDescription(i"of pure base class $pureBase"),
685-
tree.srcPos)
753+
selfType.srcPos, cs1description = " captured by this self type")
686754
super.recheckClassDef(tree, impl, cls)
687755
finally
688756
curEnv = saved
@@ -1122,6 +1190,8 @@ class CheckCaptures extends Recheck, SymTransformer:
11221190

11231191
override def needsCheck(overriding: Symbol, overridden: Symbol)(using Context): Boolean =
11241192
!setup.isPreCC(overriding) && !setup.isPreCC(overridden)
1193+
1194+
override def checkInheritedTraitParameters: Boolean = false
11251195
end OverridingPairsCheckerCC
11261196

11271197
def traverse(t: Tree)(using Context) =
@@ -1158,11 +1228,12 @@ class CheckCaptures extends Recheck, SymTransformer:
11581228
private val setup: SetupAPI = thisPhase.prev.asInstanceOf[Setup]
11591229

11601230
override def checkUnit(unit: CompilationUnit)(using Context): Unit =
1161-
setup.setupUnit(ctx.compilationUnit.tpdTree, completeDef)
1231+
setup.setupUnit(unit.tpdTree, completeDef)
1232+
collectCapturedMutVars.traverse(unit.tpdTree)
11621233

11631234
if ctx.settings.YccPrintSetup.value then
11641235
val echoHeader = "[[syntax tree at end of cc setup]]"
1165-
val treeString = show(ctx.compilationUnit.tpdTree)
1236+
val treeString = show(unit.tpdTree)
11661237
report.echo(s"$echoHeader\n$treeString\n")
11671238

11681239
withCaptureSetsExplained:
@@ -1298,6 +1369,39 @@ class CheckCaptures extends Recheck, SymTransformer:
12981369
checker.traverse(tree.knownType)
12991370
end healTypeParam
13001371

1372+
def checkNoLocalRootIn(sym: Symbol, info: Type, pos: SrcPos)(using Context): Unit =
1373+
val check = new TypeTraverser:
1374+
def traverse(tp: Type) = tp match
1375+
case tp: TermRef if tp.isLocalRootCapability =>
1376+
if tp.localRootOwner == sym then
1377+
report.error(i"local root $tp cannot appear in type of $sym", pos)
1378+
case tp: ClassInfo =>
1379+
traverseChildren(tp)
1380+
for mbr <- tp.decls do
1381+
if !mbr.is(Private) then checkNoLocalRootIn(sym, mbr.info, mbr.srcPos)
1382+
case _ =>
1383+
traverseChildren(tp)
1384+
check.traverse(info)
1385+
1386+
def checkArraysAreSealedIn(tp: Type, pos: SrcPos)(using Context): Unit =
1387+
val check = new TypeTraverser:
1388+
def traverse(t: Type): Unit =
1389+
t match
1390+
case AppliedType(tycon, arg :: Nil) if tycon.typeSymbol == defn.ArrayClass =>
1391+
if !(pos.span.isSynthetic && ctx.reporter.errorsReported)
1392+
&& !arg.typeSymbol.name.is(WildcardParamName)
1393+
then
1394+
CheckCaptures.disallowRootCapabilitiesIn(arg, NoSymbol,
1395+
"Array", "have element type",
1396+
"Since arrays are mutable, they have to be treated like variables,\nso their element type must be sealed.",
1397+
pos)
1398+
traverseChildren(t)
1399+
case defn.RefinedFunctionOf(rinfo: MethodType) =>
1400+
traverse(rinfo)
1401+
case _ =>
1402+
traverseChildren(t)
1403+
check.traverse(tp)
1404+
13011405
/** Perform the following kinds of checks
13021406
* - Check all explicitly written capturing types for well-formedness using `checkWellFormedPost`.
13031407
* - Check that arguments of TypeApplys and AppliedTypes conform to their bounds.
@@ -1309,10 +1413,11 @@ class CheckCaptures extends Recheck, SymTransformer:
13091413
val lctx = tree match
13101414
case _: DefTree | _: TypeDef if tree.symbol.exists => ctx.withOwner(tree.symbol)
13111415
case _ => ctx
1312-
traverseChildren(tree)(using lctx)
1313-
check(tree)
1416+
trace(i"post check $tree"):
1417+
traverseChildren(tree)(using lctx)
1418+
check(tree)
13141419
def check(tree: Tree)(using Context) = tree match
1315-
case t @ TypeApply(fun, args) =>
1420+
case TypeApply(fun, args) =>
13161421
fun.knownType.widen match
13171422
case tl: PolyType =>
13181423
val normArgs = args.lazyZip(tl.paramInfos).map: (arg, bounds) =>
@@ -1321,6 +1426,10 @@ class CheckCaptures extends Recheck, SymTransformer:
13211426
checkBounds(normArgs, tl)
13221427
args.lazyZip(tl.paramNames).foreach(healTypeParam(_, _, fun.symbol))
13231428
case _ =>
1429+
case _: ValOrDefDef | _: TypeDef =>
1430+
checkNoLocalRootIn(tree.symbol, tree.symbol.info, tree.symbol.srcPos)
1431+
case tree: TypeTree =>
1432+
checkArraysAreSealedIn(tree.tpe, tree.srcPos)
13241433
case _ =>
13251434
end check
13261435
end checker

compiler/src/dotty/tools/dotc/cc/Setup.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,9 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
522522
tree.symbol match
523523
case cls: ClassSymbol =>
524524
val cinfo @ ClassInfo(prefix, _, ps, decls, selfInfo) = cls.classInfo
525-
if (selfInfo eq NoType) || cls.is(ModuleClass) && !cls.isStatic then
525+
if ((selfInfo eq NoType) || cls.is(ModuleClass) && !cls.isStatic)
526+
&& !cls.isPureClass
527+
then
526528
// add capture set to self type of nested classes if no self type is given explicitly.
527529
val newSelfType = CapturingType(cinfo.selfType, CaptureSet.Var(cls))
528530
val ps1 = inContext(ctx.withOwner(cls)):
@@ -705,4 +707,5 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
705707

706708
def postCheck()(using Context): Unit =
707709
for chk <- todoAtPostCheck do chk(ctx)
710+
todoAtPostCheck.clear()
708711
end Setup

compiler/src/dotty/tools/dotc/core/Definitions.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,7 @@ class Definitions {
14431443
/** Base classes that are assumed to be pure for the purposes of capture checking.
14441444
* Every class inheriting from a pure baseclass is pure.
14451445
*/
1446-
@tu lazy val pureBaseClasses = Set(defn.ThrowableClass)
1446+
@tu lazy val pureBaseClasses = Set(ThrowableClass, PureClass)
14471447

14481448
/** Non-inheritable lasses that are assumed to be pure for the purposes of capture checking,
14491449
*/

compiler/src/dotty/tools/dotc/core/Substituters.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ object Substituters:
189189
def apply(tp: Type): Type = substThis(tp, from, to, this)(using mapCtx)
190190
}
191191

192-
final class SubstRecThisMap(from: Type, to: Type)(using Context) extends DeepTypeMap {
192+
final class SubstRecThisMap(from: Type, to: Type)(using Context) extends DeepTypeMap, IdempotentCaptRefMap {
193193
def apply(tp: Type): Type = substRecThis(tp, from, to, this)(using mapCtx)
194194
}
195195

compiler/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ trait UniqueMessagePositions extends Reporter {
2525
||
2626
dia.pos.exists
2727
&& !ctx.settings.YshowSuppressedErrors.value
28-
&& (dia.pos.start to dia.pos.end).exists(pos =>
29-
positions.get((ctx.source, pos)).exists(_.hides(dia)))
28+
&& (dia.pos.start to dia.pos.end).exists: offset =>
29+
positions.get((ctx.source, offset)).exists(_.hides(dia))
3030

3131
override def markReported(dia: Diagnostic)(using Context): Unit =
3232
if dia.pos.exists then
33-
for (pos <- dia.pos.start to dia.pos.end)
34-
positions.get(ctx.source, pos) match
33+
for offset <- dia.pos.start to dia.pos.end do
34+
positions.get((ctx.source, offset)) match
3535
case Some(dia1) if dia1.hides(dia) =>
36-
case _ => positions((ctx.source, pos)) = dia
36+
case _ => positions((ctx.source, offset)) = dia
3737
super.markReported(dia)
3838
}

0 commit comments

Comments
 (0)