Skip to content

Commit fd598cb

Browse files
authored
Merge branch 'main' into fix/i23577
2 parents 8adc284 + 69e97e9 commit fd598cb

File tree

138 files changed

+1116
-508
lines changed

Some content is hidden

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

138 files changed

+1116
-508
lines changed

compiler/src/dotty/tools/MainGenericCompiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ case class CompileSettings(
8585

8686
object MainGenericCompiler {
8787

88-
val classpathSeparator = File.pathSeparator
88+
val classpathSeparator: String = File.pathSeparator
8989

9090
@sharable val javaOption = raw"""-J(.*)""".r
9191
@sharable val javaPropOption = raw"""-D(.+?)=(.?)""".r

compiler/src/dotty/tools/MainGenericRunner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ case class Settings(
9696

9797
object MainGenericRunner {
9898

99-
val classpathSeparator = File.pathSeparator
99+
val classpathSeparator: String = File.pathSeparator
100100

101101
def processClasspath(cp: String, tail: List[String]): (List[String], List[String]) =
102102
val cpEntries = cp.split(classpathSeparator).toList

compiler/src/dotty/tools/backend/jvm/CodeGen.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
152152
new interfaces.AbstractFile {
153153
override def name = absfile.name
154154
override def path = absfile.path
155-
override def jfile = Optional.ofNullable(absfile.file)
155+
override def jfile: Optional[java.io.File] = Optional.ofNullable(absfile.file)
156156
}
157157

158158
private def genClass(cd: TypeDef, unit: CompilationUnit): ClassNode = {

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class Compiler {
171171
val rctx =
172172
if ctx.settings.Xsemanticdb.value then
173173
ctx.addMode(Mode.ReadPositions)
174-
else if ctx.settings.YcheckInitGlobal.value then
174+
else if ctx.settings.YsafeInitGlobal.value then
175175
ctx.addMode(Mode.ReadPositions)
176176
else
177177
ctx

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,7 @@ object desugar {
13471347
)).withSpan(tree.span)
13481348
end makePolyFunctionType
13491349

1350-
/** Invent a name for an anonympus given of type or template `impl`. */
1350+
/** Invent a name for an anonymous given of type or template `impl`. */
13511351
def inventGivenName(impl: Tree)(using Context): SimpleName =
13521352
val str = impl match
13531353
case impl: Template =>
@@ -2136,18 +2136,19 @@ object desugar {
21362136
* that refers to the bound variable for the pattern. Wildcard Binds are
21372137
* also replaced by Binds with fresh names.
21382138
*/
2139-
def makeIdPat(pat: Tree): (Tree, Ident) = pat match {
2140-
case bind @ Bind(name, pat1) =>
2141-
if name == nme.WILDCARD then
2142-
val name = UniqueName.fresh()
2143-
(cpy.Bind(pat)(name, pat1).withMods(bind.mods), Ident(name))
2144-
else (pat, Ident(name))
2139+
def makeIdPat(pat: Tree): (Tree, Ident) = pat match
2140+
case pat @ Bind(nme.WILDCARD, body) =>
2141+
val name =
2142+
body match
2143+
case Typed(Ident(nme.WILDCARD), tpt) if pat.mods.is(Given) => inventGivenName(tpt)
2144+
case _ => UniqueName.fresh()
2145+
(cpy.Bind(pat)(name, body).withMods(pat.mods), Ident(name))
2146+
case Bind(name, _) => (pat, Ident(name))
21452147
case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id)
21462148
case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id)
21472149
case _ =>
21482150
val name = UniqueName.fresh()
21492151
(Bind(name, pat), Ident(name))
2150-
}
21512152

21522153
/** Make a pattern filter:
21532154
* rhs.withFilter { case pat => true case _ => false }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1565,7 +1565,7 @@ object CaptureSet:
15651565
// `ref` will not seem subsumed by other capabilities in a `++`.
15661566
universal
15671567
case c: CoreCapability =>
1568-
ofType(c.underlying, followResult = true)
1568+
ofType(c.underlying, followResult = ccConfig.useSpanCapset)
15691569

15701570
/** Capture set of a type
15711571
* @param followResult If true, also include capture sets of function results.

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

Lines changed: 87 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -407,25 +407,22 @@ class CheckCaptures extends Recheck, SymTransformer:
407407
else i"references $cs1$cs1description are not all",
408408
cs1, cs2, pos, provenance)
409409

410-
/** If `sym` is a class or method nested inside a term, a capture set variable representing
411-
* the captured variables of the environment associated with `sym`.
410+
/** If `sym` is a method or a non-static inner class, a capture set variable
411+
* representing the captured variables of the environment associated with `sym`.
412412
*/
413413
def capturedVars(sym: Symbol)(using Context): CaptureSet =
414414
myCapturedVars.getOrElseUpdate(sym,
415-
if sym.ownersIterator.exists(_.isTerm)
415+
if sym.isTerm || !sym.owner.isStaticOwner
416416
then CaptureSet.Var(sym.owner, level = ccState.symLevel(sym))
417417
else CaptureSet.empty)
418418

419419
// ---- Record Uses with MarkFree ----------------------------------------------------
420420

421421
/** The next environment enclosing `env` that needs to be charged
422422
* with free references.
423-
* @param included Whether an environment is included in the range of
424-
* environments to charge. Once `included` is false, no
425-
* more environments need to be charged.
426423
*/
427-
def nextEnvToCharge(env: Env, included: Env => Boolean)(using Context): Env =
428-
if env.owner.isConstructor && included(env.outer) then env.outer.outer
424+
def nextEnvToCharge(env: Env)(using Context): Env | Null =
425+
if env.owner.isConstructor then env.outer.outer0
429426
else env.outer
430427

431428
/** A description where this environment comes from */
@@ -458,21 +455,27 @@ class CheckCaptures extends Recheck, SymTransformer:
458455
markFree(sym, sym.termRef, tree)
459456

460457
def markFree(sym: Symbol, ref: Capability, tree: Tree)(using Context): Unit =
461-
if sym.exists && ref.isTracked then markFree(ref.singletonCaptureSet, tree)
458+
if sym.exists then markFree(ref, tree)
459+
460+
def markFree(ref: Capability, tree: Tree)(using Context): Unit =
461+
if ref.isTracked then markFree(ref.singletonCaptureSet, tree)
462462

463463
/** Make sure the (projected) `cs` is a subset of the capture sets of all enclosing
464464
* environments. At each stage, only include references from `cs` that are outside
465465
* the environment's owner
466466
*/
467-
def markFree(cs: CaptureSet, tree: Tree)(using Context): Unit =
467+
def markFree(cs: CaptureSet, tree: Tree, addUseInfo: Boolean = true)(using Context): Unit =
468468
// A captured reference with the symbol `sym` is visible from the environment
469469
// if `sym` is not defined inside the owner of the environment.
470470
inline def isVisibleFromEnv(sym: Symbol, env: Env) =
471471
sym.exists && {
472+
val effectiveOwner =
473+
if env.owner.isConstructor then env.owner.owner
474+
else env.owner
472475
if env.kind == EnvKind.NestedInOwner then
473-
!sym.isProperlyContainedIn(env.owner)
476+
!sym.isProperlyContainedIn(effectiveOwner)
474477
else
475-
!sym.isContainedIn(env.owner)
478+
!sym.isContainedIn(effectiveOwner)
476479
}
477480

478481
/** Avoid locally defined capability by charging the underlying type
@@ -493,7 +496,7 @@ class CheckCaptures extends Recheck, SymTransformer:
493496
case _ => c.core match
494497
case c1: RootCapability => c1.singletonCaptureSet
495498
case c1: CoreCapability =>
496-
CaptureSet.ofType(c1.widen, followResult = true)
499+
CaptureSet.ofType(c1.widen, followResult = ccConfig.useSpanCapset)
497500
capt.println(i"Widen reach $c to $underlying in ${env.owner}")
498501
underlying.disallowBadRoots(NoSymbol): () =>
499502
report.error(em"Local capability $c${env.owner.qualString("in")} cannot have `cap` as underlying capture set", tree.srcPos)
@@ -535,13 +538,15 @@ class CheckCaptures extends Recheck, SymTransformer:
535538
checkSubset(included, env.captured, tree.srcPos, provenance(env))
536539
capt.println(i"Include call or box capture $included from $cs in ${env.owner} --> ${env.captured}")
537540
if !isOfNestedMethod(env) then
538-
recur(included, nextEnvToCharge(env, !_.owner.isStaticOwner), env)
541+
val nextEnv = nextEnvToCharge(env)
542+
if nextEnv != null && !nextEnv.owner.isStaticOwner then
543+
recur(included, nextEnv, env)
539544
// Under deferredReaches, don't propagate out of methods inside terms.
540545
// The use set of these methods will be charged when that method is called.
541546

542547
if !cs.isAlwaysEmpty then
543548
recur(cs, curEnv, null)
544-
useInfos += ((tree, cs, curEnv))
549+
if addUseInfo then useInfos += ((tree, cs, curEnv))
545550
end markFree
546551

547552
/** If capability `c` refers to a parameter that is not implicitly or explicitly
@@ -626,25 +631,33 @@ class CheckCaptures extends Recheck, SymTransformer:
626631
// If ident refers to a parameterless method, charge its cv to the environment
627632
includeCallCaptures(sym, sym.info, tree)
628633
else if !sym.isStatic then
629-
// Otherwise charge its symbol, but add all selections and also any `.rd`
630-
// modifier implied by the expected type `pt`.
631-
// Example: If we have `x` and the expected type says we select that with `.a.b`
632-
// where `b` is a read-only method, we charge `x.a.b.rd` instead of `x`.
633-
def addSelects(ref: TermRef, pt: Type): Capability = pt match
634-
case pt: PathSelectionProto if ref.isTracked =>
635-
if pt.sym.isReadOnlyMethod then
636-
ref.readOnly
637-
else
638-
// if `ref` is not tracked then the selection could not give anything new
639-
// class SerializationProxy in stdlib-cc/../LazyListIterable.scala has an example where this matters.
640-
addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt)
641-
case _ => ref
642-
var pathRef: Capability = addSelects(sym.termRef, pt)
643-
if pathRef.derivesFromMutable && pt.isValueType && !pt.isMutableType then
644-
pathRef = pathRef.readOnly
645-
markFree(sym, pathRef, tree)
634+
markFree(sym, pathRef(sym.termRef, pt), tree)
646635
mapResultRoots(super.recheckIdent(tree, pt), tree.symbol)
647636

637+
override def recheckThis(tree: This, pt: Type)(using Context): Type =
638+
markFree(pathRef(tree.tpe.asInstanceOf[ThisType], pt), tree)
639+
super.recheckThis(tree, pt)
640+
641+
/** Add all selections and also any `.rd modifier implied by the expected
642+
* type `pt` to `base`. Example:
643+
* If we have `x` and the expected type says we select that with `.a.b`
644+
* where `b` is a read-only method, we charge `x.a.b.rd` instead of `x`.
645+
*/
646+
private def pathRef(base: TermRef | ThisType, pt: Type)(using Context): Capability =
647+
def addSelects(ref: TermRef | ThisType, pt: Type): Capability = pt match
648+
case pt: PathSelectionProto if ref.isTracked =>
649+
if pt.sym.isReadOnlyMethod then
650+
ref.readOnly
651+
else
652+
// if `ref` is not tracked then the selection could not give anything new
653+
// class SerializationProxy in stdlib-cc/../LazyListIterable.scala has an example where this matters.
654+
addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt)
655+
case _ => ref
656+
val ref: Capability = addSelects(base, pt)
657+
if ref.derivesFromMutable && pt.isValueType && !pt.isMutableType
658+
then ref.readOnly
659+
else ref
660+
648661
/** The expected type for the qualifier of a selection. If the selection
649662
* could be part of a capability path or is a a read-only method, we return
650663
* a PathSelectionProto.
@@ -866,7 +879,7 @@ class CheckCaptures extends Recheck, SymTransformer:
866879
val (refined, cs) = addParamArgRefinements(core, initCs)
867880
refined.capturing(cs)
868881

869-
augmentConstructorType(resType, capturedVars(cls) ++ capturedVars(constr))
882+
augmentConstructorType(resType, capturedVars(cls))
870883
.showing(i"constr type $mt with $argTypes%, % in $constr = $result", capt)
871884
end refineConstructorInstance
872885

@@ -975,6 +988,8 @@ class CheckCaptures extends Recheck, SymTransformer:
975988
* - Interpolate contravariant capture set variables in result type.
976989
*/
977990
override def recheckValDef(tree: ValDef, sym: Symbol)(using Context): Type =
991+
val savedEnv = curEnv
992+
val runInConstructor = !sym.isOneOf(Param | ParamAccessor | Lazy | NonMember)
978993
try
979994
if sym.is(Module) then sym.info // Modules are checked by checking the module class
980995
else
@@ -993,6 +1008,8 @@ class CheckCaptures extends Recheck, SymTransformer:
9931008
""
9941009
disallowBadRootsIn(
9951010
tree.tpt.nuType, NoSymbol, i"Mutable $sym", "have type", addendum, sym.srcPos)
1011+
if runInConstructor then
1012+
pushConstructorEnv()
9961013
checkInferredResult(super.recheckValDef(tree, sym), tree)
9971014
finally
9981015
if !sym.is(Param) then
@@ -1002,6 +1019,22 @@ class CheckCaptures extends Recheck, SymTransformer:
10021019
// function is compiled since we do not propagate expected types into blocks.
10031020
interpolateIfInferred(tree.tpt, sym)
10041021

1022+
def declaredCaptures = tree.tpt.nuType.captureSet
1023+
if runInConstructor && savedEnv.owner.isClass then
1024+
curEnv = savedEnv
1025+
markFree(declaredCaptures, tree, addUseInfo = false)
1026+
1027+
if sym.owner.isStaticOwner && !declaredCaptures.elems.isEmpty && sym != defn.captureRoot then
1028+
def where =
1029+
if sym.effectiveOwner.is(Package) then "top-level definition"
1030+
else i"member of static ${sym.owner}"
1031+
report.warning(
1032+
em"""$sym has a non-empty capture set but will not be added as
1033+
|a capability to computed capture sets since it is globally accessible
1034+
|as a $where. Global values cannot be capabilities.""",
1035+
tree.namePos)
1036+
end recheckValDef
1037+
10051038
/** Recheck method definitions:
10061039
* - check body in a nested environment that tracks uses, in a nested level,
10071040
* and in a nested context that knows abaout Contains parameters so that we
@@ -1228,6 +1261,24 @@ class CheckCaptures extends Recheck, SymTransformer:
12281261
recheckFinish(result, arg, pt)
12291262
*/
12301263

1264+
/** If environment is owned by a class, run in a new environment owned by
1265+
* its primary constructor instead.
1266+
*/
1267+
def pushConstructorEnv()(using Context): Unit =
1268+
if curEnv.owner.isClass then
1269+
val constr = curEnv.owner.primaryConstructor
1270+
if constr.exists then
1271+
val constrSet = capturedVars(constr)
1272+
if capturedVars(constr) ne CaptureSet.empty then
1273+
curEnv = Env(constr, EnvKind.Regular, constrSet, curEnv)
1274+
1275+
override def recheckStat(stat: Tree)(using Context): Unit =
1276+
val saved = curEnv
1277+
if !stat.isInstanceOf[MemberDef] then
1278+
pushConstructorEnv()
1279+
try recheck(stat)
1280+
finally curEnv = saved
1281+
12311282
/** The main recheck method does some box adapation for all nodes:
12321283
* - If expected type `pt` is boxed and the tree is a lambda or a reference,
12331284
* don't propagate free variables.
@@ -2021,7 +2072,9 @@ class CheckCaptures extends Recheck, SymTransformer:
20212072
if env.kind == EnvKind.Boxed then env.owner
20222073
else if isOfNestedMethod(env) then env.owner.owner
20232074
else if env.owner.isStaticOwner then NoSymbol
2024-
else boxedOwner(nextEnvToCharge(env, alwaysTrue))
2075+
else
2076+
val nextEnv = nextEnvToCharge(env)
2077+
if nextEnv == null then NoSymbol else boxedOwner(nextEnv)
20252078

20262079
def checkUseUnlessBoxed(c: Capability, croot: NamedType) =
20272080
if !boxedOwner(env).isContainedIn(croot.symbol.owner) then

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

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
459459
if sym.isType then stripImpliedCaptureSet(tp2)
460460
else tp2
461461
if freshen then
462-
capToFresh(tp3, Origin.InDecl(sym)).tap(addOwnerAsHidden(_, sym))
462+
capToFresh(tp3, Origin.InDecl(sym))
463463
else tp3
464464
end transformExplicitType
465465

@@ -473,25 +473,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
473473
extension (sym: Symbol) def nextInfo(using Context): Type =
474474
atPhase(thisPhase.next)(sym.info)
475475

476-
private def addOwnerAsHidden(tp: Type, owner: Symbol)(using Context): Unit =
477-
val ref = owner.termRef
478-
def add = new TypeTraverser:
479-
var reach = false
480-
def traverse(t: Type): Unit = t match
481-
case t @ CapturingType(parent, refs) =>
482-
val saved = reach
483-
reach |= t.isBoxed
484-
try
485-
traverse(parent)
486-
for case fresh: FreshCap <- refs.elems.iterator do // TODO: what about fresh.rd elems?
487-
if reach then fresh.hiddenSet.elems += ref.reach
488-
else if ref.isTracked then fresh.hiddenSet.elems += ref
489-
finally reach = saved
490-
case _ =>
491-
traverseChildren(t)
492-
if ref.isTrackableRef then add.traverse(tp)
493-
end addOwnerAsHidden
494-
495476
/** A traverser that adds knownTypes and updates symbol infos */
496477
def setupTraverser(checker: CheckerAPI) = new TreeTraverserWithPreciseImportContexts:
497478
import checker.*

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ object ccConfig:
4343
*/
4444
inline val postCheckCapturesets = false
4545

46+
/** If true take as the underlying capture set of a capability of function type
47+
* the capture set along the span, including capture sets of function results.
48+
*/
49+
inline val useSpanCapset = false
50+
4651
/** If true, do level checking for FreshCap instances */
4752
def useFreshLevels(using Context): Boolean =
4853
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`)

0 commit comments

Comments
 (0)