Skip to content

Commit

Permalink
Add logic to mark paths as used
Browse files Browse the repository at this point in the history
If we refer to a path `a.b`, we should mark `a.b` as used,
which is better than marking `a`.
  • Loading branch information
odersky committed Sep 25, 2024
1 parent 61ca309 commit c08dee2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 18 deletions.
27 changes: 19 additions & 8 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Trees.*
import typer.RefChecks.{checkAllOverrides, checkSelfAgainstParents, OverridingPairsChecker}
import typer.Checking.{checkBounds, checkAppliedTypesIn}
import typer.ErrorReporting.{Addenda, NothingToAdd, err}
import typer.ProtoTypes.{AnySelectionProto, LhsProto}
import typer.ProtoTypes.{LhsProto, WildcardSelectionProto}
import util.{SimpleIdentitySet, EqHashMap, EqHashSet, SrcPos, Property}
import transform.{Recheck, PreRecheck, CapturedVars}
import Recheck.*
Expand Down Expand Up @@ -183,6 +183,9 @@ object CheckCaptures:
/** Attachment key for bodies of closures, provided they are values */
val ClosureBodyValue = Property.Key[Unit]

/** A prototype that indicates selection with an immutable value */
class PathSelectionProto(val sym: Symbol, val pt: Type)(using Context) extends WildcardSelectionProto

class CheckCaptures extends Recheck, SymTransformer:
thisPhase =>

Expand Down Expand Up @@ -357,12 +360,13 @@ class CheckCaptures extends Recheck, SymTransformer:
* the environment in which `sym` is defined.
*/
def markFree(sym: Symbol, pos: SrcPos)(using Context): Unit =
if sym.exists then
val ref = sym.termRef
if ref.isTracked then
forallOuterEnvsUpTo(sym.enclosure): env =>
capt.println(i"Mark $sym with cs ${ref.captureSet} free in ${env.owner}")
checkElem(ref, env.captured, pos, provenance(env))
markFree(sym, sym.termRef, pos)

def markFree(sym: Symbol, ref: TermRef, pos: SrcPos)(using Context): Unit =
if sym.exists && ref.isTracked then
forallOuterEnvsUpTo(sym.enclosure): env =>
capt.println(i"Mark $sym with cs ${ref.captureSet} free in ${env.owner}")
checkElem(ref, env.captured, pos, provenance(env))

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

override def selectionProto(tree: Select, pt: Type)(using Context): Type =
if !tree.symbol.isOneOf(UnstableValueFlags) then PathSelectionProto(tree.symbol, pt)
else super.selectionProto(tree, pt)

/** A specialized implementation of the selection rule.
*
* E |- f: T{ m: R^Cr }^{f}
Expand Down
15 changes: 7 additions & 8 deletions compiler/src/dotty/tools/dotc/transform/Recheck.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import DenotTransformers.{DenotTransformer, IdentityDenotTransformer, SymTransfo
import NamerOps.linkConstructorParams
import NullOpsDecorator.stripNull
import typer.ErrorReporting.err
import typer.ProtoTypes.*
import typer.ProtoTypes.{AnySelectionProto, LhsProto}
import typer.TypeAssigner.seqLitType
import typer.ConstFold
import typer.ErrorReporting.{Addenda, NothingToAdd}
Expand Down Expand Up @@ -203,13 +203,12 @@ abstract class Recheck extends Phase, SymTransformer:
tree.tpe

def recheckSelect(tree: Select, pt: Type)(using Context): Type =
recheckSelection(tree, recheckSelectQualifier(tree), tree.name, pt)
recheckSelection(tree,
recheck(tree.qualifier, selectionProto(tree, pt)).widenIfUnstable,
tree.name, pt)

def recheckSelectQualifier(tree: Select)(using Context): Type =
val proto =
if tree.symbol == defn.Any_asInstanceOf then WildcardType
else AnySelectionProto
recheck(tree.qualifier, proto).widenIfUnstable
def selectionProto(tree: Select, pt: Type)(using Context): Type =
if tree.symbol == defn.Any_asInstanceOf then WildcardType else AnySelectionProto

def recheckSelection(tree: Select, qualType: Type, name: Name,
sharpen: Denotation => Denotation)(using Context): Type =
Expand Down Expand Up @@ -308,7 +307,7 @@ abstract class Recheck extends Phase, SymTransformer:
def recheckApply(tree: Apply, pt: Type)(using Context): Type =
val (funtpe0, qualType) = tree.fun match
case fun: Select =>
val qualType = recheckSelectQualifier(fun)
val qualType = recheck(fun.qualifier, selectionProto(fun, WildcardType)).widenIfUnstable
(recheckSelection(fun, qualType, fun.name, WildcardType), qualType)
case _ =>
(recheck(tree.fun), NoType)
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ object ProtoTypes {
case tp: UnapplyFunProto => new UnapplySelectionProto(name, nameSpan)
case tp => SelectionProto(name, IgnoredProto(tp), typer, privateOK = true, nameSpan)

class WildcardSelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)

/** A prototype for expressions [] that are in some unspecified selection operation
*
* [].?: ?
Expand All @@ -332,9 +334,9 @@ object ProtoTypes {
* operation is further selection. In this case, the expression need not be a value.
* @see checkValue
*/
@sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)
@sharable object AnySelectionProto extends WildcardSelectionProto

@sharable object SingletonTypeProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed, true, NoSpan)
@sharable object SingletonTypeProto extends WildcardSelectionProto

/** A prototype for selections in pattern constructors */
class UnapplySelectionProto(name: Name, nameSpan: Span) extends SelectionProto(name, WildcardType, NoViewsAllowed, true, nameSpan)
Expand Down
19 changes: 19 additions & 0 deletions tests/pos-custom-args/captures/path-use.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import language.experimental.namedTuples

class IO

class C(val f: IO^):
val procs: List[Proc] = ???

type Proc = () => Unit

def test(io: IO^) =
val c = C(io)
val f = () => println(c.f)
val _: () ->{c.f} Unit = f

val x = c.procs
val _: List[() ->{c.procs*} Unit] = x

val g = () => println(c.procs.head)
val _: () ->{c.procs*} Unit = g

0 comments on commit c08dee2

Please sign in to comment.