Skip to content

Commit c08dee2

Browse files
committed
Add logic to mark paths as used
If we refer to a path `a.b`, we should mark `a.b` as used, which is better than marking `a`.
1 parent 61ca309 commit c08dee2

File tree

4 files changed

+49
-18
lines changed

4 files changed

+49
-18
lines changed

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

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Trees.*
1313
import typer.RefChecks.{checkAllOverrides, checkSelfAgainstParents, OverridingPairsChecker}
1414
import typer.Checking.{checkBounds, checkAppliedTypesIn}
1515
import typer.ErrorReporting.{Addenda, NothingToAdd, err}
16-
import typer.ProtoTypes.{AnySelectionProto, LhsProto}
16+
import typer.ProtoTypes.{LhsProto, WildcardSelectionProto}
1717
import util.{SimpleIdentitySet, EqHashMap, EqHashSet, SrcPos, Property}
1818
import transform.{Recheck, PreRecheck, CapturedVars}
1919
import Recheck.*
@@ -183,6 +183,9 @@ object CheckCaptures:
183183
/** Attachment key for bodies of closures, provided they are values */
184184
val ClosureBodyValue = Property.Key[Unit]
185185

186+
/** A prototype that indicates selection with an immutable value */
187+
class PathSelectionProto(val sym: Symbol, val pt: Type)(using Context) extends WildcardSelectionProto
188+
186189
class CheckCaptures extends Recheck, SymTransformer:
187190
thisPhase =>
188191

@@ -357,12 +360,13 @@ class CheckCaptures extends Recheck, SymTransformer:
357360
* the environment in which `sym` is defined.
358361
*/
359362
def markFree(sym: Symbol, pos: SrcPos)(using Context): Unit =
360-
if sym.exists then
361-
val ref = sym.termRef
362-
if ref.isTracked then
363-
forallOuterEnvsUpTo(sym.enclosure): env =>
364-
capt.println(i"Mark $sym with cs ${ref.captureSet} free in ${env.owner}")
365-
checkElem(ref, env.captured, pos, provenance(env))
363+
markFree(sym, sym.termRef, pos)
364+
365+
def markFree(sym: Symbol, ref: TermRef, pos: SrcPos)(using Context): Unit =
366+
if sym.exists && ref.isTracked then
367+
forallOuterEnvsUpTo(sym.enclosure): env =>
368+
capt.println(i"Mark $sym with cs ${ref.captureSet} free in ${env.owner}")
369+
checkElem(ref, env.captured, pos, provenance(env))
366370

367371
/** Make sure (projected) `cs` is a subset of the capture sets of all enclosing
368372
* environments. At each stage, only include references from `cs` that are outside
@@ -464,9 +468,16 @@ class CheckCaptures extends Recheck, SymTransformer:
464468
includeCallCaptures(tree.symbol, tree.srcPos)
465469
else
466470
//debugShowEnvs()
467-
markFree(tree.symbol, tree.srcPos)
471+
def addSelects(ref: TermRef, pt: Type): TermRef = pt match
472+
case pt: PathSelectionProto => addSelects(ref.select(pt.sym).asInstanceOf[TermRef], pt.pt)
473+
case _ => ref
474+
markFree(tree.symbol, addSelects(tree.symbol.termRef, pt), tree.srcPos)
468475
super.recheckIdent(tree, pt)
469476

477+
override def selectionProto(tree: Select, pt: Type)(using Context): Type =
478+
if !tree.symbol.isOneOf(UnstableValueFlags) then PathSelectionProto(tree.symbol, pt)
479+
else super.selectionProto(tree, pt)
480+
470481
/** A specialized implementation of the selection rule.
471482
*
472483
* E |- f: T{ m: R^Cr }^{f}

compiler/src/dotty/tools/dotc/transform/Recheck.scala

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import DenotTransformers.{DenotTransformer, IdentityDenotTransformer, SymTransfo
1212
import NamerOps.linkConstructorParams
1313
import NullOpsDecorator.stripNull
1414
import typer.ErrorReporting.err
15-
import typer.ProtoTypes.*
15+
import typer.ProtoTypes.{AnySelectionProto, LhsProto}
1616
import typer.TypeAssigner.seqLitType
1717
import typer.ConstFold
1818
import typer.ErrorReporting.{Addenda, NothingToAdd}
@@ -203,13 +203,12 @@ abstract class Recheck extends Phase, SymTransformer:
203203
tree.tpe
204204

205205
def recheckSelect(tree: Select, pt: Type)(using Context): Type =
206-
recheckSelection(tree, recheckSelectQualifier(tree), tree.name, pt)
206+
recheckSelection(tree,
207+
recheck(tree.qualifier, selectionProto(tree, pt)).widenIfUnstable,
208+
tree.name, pt)
207209

208-
def recheckSelectQualifier(tree: Select)(using Context): Type =
209-
val proto =
210-
if tree.symbol == defn.Any_asInstanceOf then WildcardType
211-
else AnySelectionProto
212-
recheck(tree.qualifier, proto).widenIfUnstable
210+
def selectionProto(tree: Select, pt: Type)(using Context): Type =
211+
if tree.symbol == defn.Any_asInstanceOf then WildcardType else AnySelectionProto
213212

214213
def recheckSelection(tree: Select, qualType: Type, name: Name,
215214
sharpen: Denotation => Denotation)(using Context): Type =
@@ -308,7 +307,7 @@ abstract class Recheck extends Phase, SymTransformer:
308307
def recheckApply(tree: Apply, pt: Type)(using Context): Type =
309308
val (funtpe0, qualType) = tree.fun match
310309
case fun: Select =>
311-
val qualType = recheckSelectQualifier(fun)
310+
val qualType = recheck(fun.qualifier, selectionProto(fun, WildcardType)).widenIfUnstable
312311
(recheckSelection(fun, qualType, fun.name, WildcardType), qualType)
313312
case _ =>
314313
(recheck(tree.fun), NoType)

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ object ProtoTypes {
324324
case tp: UnapplyFunProto => new UnapplySelectionProto(name, nameSpan)
325325
case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true, nameSpan)
326326

327+
class WildcardSelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)
328+
327329
/** A prototype for expressions [] that are in some unspecified selection operation
328330
*
329331
* [].?: ?
@@ -332,9 +334,9 @@ object ProtoTypes {
332334
* operation is further selection. In this case, the expression need not be a value.
333335
* @see checkValue
334336
*/
335-
@sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)
337+
@sharable object AnySelectionProto extends WildcardSelectionProto
336338

337-
@sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)
339+
@sharable object SingletonTypeProto extends WildcardSelectionProto
338340

339341
/** A prototype for selections in pattern constructors */
340342
class UnapplySelectionProto(name: Name, nameSpan: Span) extends SelectionProto(name, WildcardType, NoViewsAllowed, true, nameSpan)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import language.experimental.namedTuples
2+
3+
class IO
4+
5+
class C(val f: IO^):
6+
val procs: List[Proc] = ???
7+
8+
type Proc = () => Unit
9+
10+
def test(io: IO^) =
11+
val c = C(io)
12+
val f = () => println(c.f)
13+
val _: () ->{c.f} Unit = f
14+
15+
val x = c.procs
16+
val _: List[() ->{c.procs*} Unit] = x
17+
18+
val g = () => println(c.procs.head)
19+
val _: () ->{c.procs*} Unit = g

0 commit comments

Comments
 (0)