Skip to content

Commit 6df16c8

Browse files
committed
Refactor: Instead of purpose string, use Origin enum
1 parent 4705a47 commit 6df16c8

File tree

8 files changed

+88
-50
lines changed

8 files changed

+88
-50
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ extension (tp: Type)
464464
// preceding arguments are known to be always pure
465465
t.derivedFunctionOrMethod(
466466
args,
467-
apply(root.resultToFresh(res, i"when instantiating $t")))
467+
apply(root.resultToFresh(res, root.Origin.ResultInstance(t, NoSymbol))))
468468
else
469469
t
470470
case _ =>
@@ -798,7 +798,7 @@ abstract class DeepTypeAccumulator[T](using Context) extends TypeAccumulator[T]:
798798
this(acc, parent)
799799
case t @ FunctionOrMethod(args, res) =>
800800
if args.forall(_.isAlwaysPure) then
801-
this(acc, root.resultToFresh(res, i" when instantiating $t"))
801+
this(acc, root.resultToFresh(res, root.Origin.ResultInstance(t, NoSymbol)))
802802
else acc
803803
case _ =>
804804
foldOver(acc, t)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ object CaptureSet:
423423
def universalImpliedByCapability(using Context) =
424424
defn.universalCSImpliedByCapability
425425

426-
def fresh(purpose: => String)(using Context): CaptureSet =
427-
root.Fresh(purpose).singletonCaptureSet
426+
def fresh(origin: root.Origin)(using Context): CaptureSet =
427+
root.Fresh(origin).singletonCaptureSet
428428

429429
/** The shared capture set `{cap.rd}` */
430430
def shared(using Context): CaptureSet =
@@ -685,7 +685,7 @@ object CaptureSet:
685685
def solve()(using Context): Unit =
686686
CCState.withCapAsRoot: // // OK here since we infer parameter types that get checked later
687687
val approx = upperApprox(empty)
688-
.map(root.CapToFresh("").inverse) // Fresh --> cap
688+
.map(root.CapToFresh(root.Origin.Unknown).inverse) // Fresh --> cap
689689
.showing(i"solve $this = $result", capt)
690690
//println(i"solving var $this $approx ${approx.isConst} deps = ${deps.toList}")
691691
val newElems = approx.elems -- elems
@@ -1388,7 +1388,7 @@ object CaptureSet:
13881388
def capturingCase(acc: CaptureSet, parent: Type, refs: CaptureSet) =
13891389
this(acc, parent) ++ refs
13901390
def abstractTypeCase(acc: CaptureSet, t: TypeRef, upperBound: Type) =
1391-
if includeTypevars && upperBound.isExactlyAny then CaptureSet.fresh(i" when computing deep capture set of $t")
1391+
if includeTypevars && upperBound.isExactlyAny then fresh(root.Origin.DeepCS(t))
13921392
else this(acc, upperBound)
13931393
collect(CaptureSet.empty, tp)
13941394

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

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ class CheckCaptures extends Recheck, SymTransformer:
714714
val meth = tree.fun.symbol
715715
if meth == defn.Caps_unsafeAssumePure then
716716
val arg :: Nil = tree.args: @unchecked
717-
val argType0 = recheck(arg, pt.stripCapturing.capturing(root.Fresh(i" when instantiating argument of unsafeAssumePure")))
717+
val argType0 = recheck(arg, pt.stripCapturing.capturing(root.Fresh(root.Origin.UnsafeAssumePure)))
718718
val argType =
719719
if argType0.captureSet.isAlwaysEmpty then argType0
720720
else argType0.widen.stripCapturing
@@ -730,11 +730,7 @@ class CheckCaptures extends Recheck, SymTransformer:
730730
* charge the deep capture set of the actual argument to the environment.
731731
*/
732732
protected override def recheckArg(arg: Tree, formal: Type, pref: ParamRef, app: Apply)(using Context): Type =
733-
val fn = app.symbol
734-
def purpose =
735-
if fn.exists then i" when checking argument to parameter ${pref.paramName} of $fn"
736-
else ""
737-
val freshenedFormal = root.capToFresh(formal, purpose)
733+
val freshenedFormal = root.capToFresh(formal, root.Origin.Formal(pref, app))
738734
val argType = recheck(arg, freshenedFormal)
739735
.showing(i"recheck arg $arg vs $freshenedFormal = $result", capt)
740736
if formal.hasAnnotation(defn.UseAnnot) || formal.hasAnnotation(defn.ConsumeAnnot) then
@@ -768,11 +764,9 @@ class CheckCaptures extends Recheck, SymTransformer:
768764
*/
769765
protected override
770766
def recheckApplication(tree: Apply, qualType: Type, funType: MethodType, argTypes: List[Type])(using Context): Type =
771-
val fn = tree.symbol
772-
def methDescr = if fn.exists then i"$fn's type " else ""
773767
val appType = root.resultToFresh(
774768
super.recheckApplication(tree, qualType, funType, argTypes),
775-
i" when instantiating $methDescr$funType")
769+
root.Origin.ResultInstance(funType, tree.symbol))
776770
val qualCaptures = qualType.captureSet
777771
val argCaptures =
778772
for (argType, formal) <- argTypes.lazyZip(funType.paramInfos) yield
@@ -832,9 +826,9 @@ class CheckCaptures extends Recheck, SymTransformer:
832826
var refined: Type = core
833827
var allCaptures: CaptureSet =
834828
if core.derivesFromMutable then
835-
initCs ++ CaptureSet.fresh(i" when constructing mutable $core")
829+
initCs ++ root.Fresh(root.Origin.NewMutable(core)).singletonCaptureSet
836830
else if core.derivesFromCapability then
837-
initCs ++ root.Fresh(i" when constructing Capability instance $core").readOnly.singletonCaptureSet
831+
initCs ++ root.Fresh(root.Origin.NewCapability(core)).readOnly.singletonCaptureSet
838832
else initCs
839833
for (getterName, argType) <- mt.paramNames.lazyZip(argTypes) do
840834
val getter = cls.info.member(getterName).suchThat(_.isRefiningParamAccessor).symbol
@@ -877,7 +871,7 @@ class CheckCaptures extends Recheck, SymTransformer:
877871
def methDescr = if meth.exists then i"$meth's type " else ""
878872
disallowCapInTypeArgs(tree.fun, meth, tree.args)
879873
val funType = super.recheckTypeApply(tree, pt)
880-
val res = root.resultToFresh(funType, i" when instantiating $methDescr$funType")
874+
val res = root.resultToFresh(funType, root.Origin.ResultInstance(funType, meth))
881875
includeCallCaptures(tree.symbol, res, tree)
882876
checkContains(tree)
883877
res
@@ -943,10 +937,8 @@ class CheckCaptures extends Recheck, SymTransformer:
943937
rinfo.instantiate(paramTypes)
944938
case _ =>
945939
resType
946-
val respt = root.resultToFresh(respt0,
947-
i" when instantiating expected result type $respt0 of lambda")
948-
val res = root.resultToFresh(mdef.tpt.nuType,
949-
i" when instantiating result type ${mdef.tpt.nuType} of lambda")
940+
val respt = root.resultToFresh(respt0, root.Origin.LambdaExpected(respt0))
941+
val res = root.resultToFresh(mdef.tpt.nuType, root.Origin.LambdaActual(mdef.tpt.nuType))
950942
// We need to open existentials here in order not to get vars mixed up in them
951943
// We do the proper check with existentials when we are finished with the closure block.
952944
capt.println(i"pre-check closure $expr of type $res against $respt")
@@ -1647,8 +1639,7 @@ class CheckCaptures extends Recheck, SymTransformer:
16471639
otherTp.derivedTypeBounds(
16481640
otherTp.lo,
16491641
hi.derivedCapturingType(parent,
1650-
root.Fresh(i" when instantiating upper bound of member overridden by $member")
1651-
.singletonCaptureSet))))
1642+
CaptureSet.fresh(root.Origin.OverriddenType(member))))))
16521643
case _ => None
16531644
case _ => None
16541645
case _ => None

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -473,11 +473,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
473473
if sym.isType then stripImpliedCaptureSet(tp2)
474474
else tp2
475475
if freshen then
476-
root.capToFresh(tp3,
477-
if sym.is(Method) then i" in the result type of $sym"
478-
else if sym.exists then i" in the type of $sym"
479-
else "")
480-
.tap(addOwnerAsHidden(_, sym))
476+
root.capToFresh(tp3, root.Origin.InDecl(sym)).tap(addOwnerAsHidden(_, sym))
481477
else tp3
482478
end transformExplicitType
483479

@@ -574,7 +570,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
574570
for case arg: TypeTree <- args do
575571
if defn.isTypeTestOrCast(fn.symbol) then
576572
arg.setNuType(
577-
root.capToFresh(arg.tpe, i" of type argument ${arg.tpe}"))
573+
root.capToFresh(arg.tpe, root.Origin.TypeArg(arg.tpe)))
578574
else
579575
transformTT(arg, NoSymbol, boxed = true) // type arguments in type applications are boxed
580576

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ object Synthetics:
117117
case info: MethodType =>
118118
val paramInfo :: Nil = info.paramInfos: @unchecked
119119
val newParamInfo = CapturingType(paramInfo,
120-
CaptureSet.fresh(i" when instantiating argument of unapply with type $info"))
120+
CaptureSet.fresh(root.Origin.UnapplyInstance(info)))
121121
val trackedParam = info.paramRefs.head
122122
def newResult(tp: Type): Type = tp match
123123
case tp: MethodOrPoly =>

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

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import NameOps.isImpureFunction
1414
import reporting.Message
1515
import util.{SimpleIdentitySet, EqHashMap}
1616
import util.Spans.NoSpan
17+
import ast.tpd
1718
import annotation.constructorOnly
1819

1920
/** A module defining three kinds of root capabilities
@@ -59,9 +60,58 @@ import annotation.constructorOnly
5960
*/
6061
object root:
6162

63+
enum Origin:
64+
case InDecl(sym: Symbol)
65+
case TypeArg(tp: Type)
66+
case UnsafeAssumePure
67+
case Formal(pref: ParamRef, app: tpd.Apply)
68+
case ResultInstance(methType: Type, meth: Symbol)
69+
case UnapplyInstance(info: MethodType)
70+
case NewMutable(tp: Type)
71+
case NewCapability(tp: Type)
72+
case LambdaExpected(respt: Type)
73+
case LambdaActual(restp: Type)
74+
case OverriddenType(member: Symbol)
75+
case DeepCS(ref: TypeRef)
76+
case Unknown
77+
78+
def explanation(using Context): String = this match
79+
case InDecl(sym: Symbol) =>
80+
if sym.is(Method) then i" in the result type of $sym"
81+
else if sym.exists then i" in the type of $sym"
82+
else ""
83+
case TypeArg(tp: Type) =>
84+
i" of type argument $tp"
85+
case UnsafeAssumePure =>
86+
" when instantiating argument of unsafeAssumePure"
87+
case Formal(pref, app) =>
88+
val meth = app.symbol
89+
if meth.exists
90+
then i" when checking argument to parameter ${pref.paramName} of $meth"
91+
else ""
92+
case ResultInstance(mt, meth) =>
93+
val methDescr = if meth.exists then i"$meth's type " else ""
94+
i" when instantiating $methDescr$mt"
95+
case UnapplyInstance(info) =>
96+
i" when instantiating argument of unapply with type $info"
97+
case NewMutable(tp) =>
98+
i" when constructing mutable $tp"
99+
case NewCapability(tp) =>
100+
i" when constructing Capability instance $tp"
101+
case LambdaExpected(respt) =>
102+
i" when instantiating expected result type $respt of lambda"
103+
case LambdaActual(restp: Type) =>
104+
i" when instantiating result type $restp of lambda"
105+
case OverriddenType(member: Symbol) =>
106+
i" when instantiating upper bound of member overridden by $member"
107+
case DeepCS(ref: TypeRef) =>
108+
i" when computing deep capture set of $ref"
109+
case Unknown =>
110+
""
111+
62112
enum Kind:
63113
case Result(binder: MethodType)
64-
case Fresh(hidden: CaptureSet.HiddenSet)(val purpose: () => String)
114+
case Fresh(hidden: CaptureSet.HiddenSet)(val origin: Origin)
65115
case Global
66116

67117
override def equals(other: Any): Boolean =
@@ -129,10 +179,10 @@ object root:
129179

130180
/** Constructor and extractor methods for "fresh" capabilities */
131181
object Fresh:
132-
def apply(using Context)(purpose: => String, owner: Symbol = ctx.owner): CaptureRef =
182+
def apply(using Context)(origin: Origin, owner: Symbol = ctx.owner): CaptureRef =
133183
if ccConfig.useSepChecks then
134184
val hiddenSet = CaptureSet.HiddenSet(owner)
135-
val res = AnnotatedType(cap, Annot(Kind.Fresh(hiddenSet)(() => purpose)))
185+
val res = AnnotatedType(cap, Annot(Kind.Fresh(hiddenSet)(origin)))
136186
hiddenSet.owningCap = res
137187
//assert(hiddenSet.id != 3)
138188
res
@@ -167,14 +217,14 @@ object root:
167217
/** Map each occurrence of cap to a different Fresh instance
168218
* Exception: CapSet^ stays as it is.
169219
*/
170-
class CapToFresh(purpose: => String)(using Context) extends BiTypeMap, FollowAliasesMap:
220+
class CapToFresh(origin: Origin)(using Context) extends BiTypeMap, FollowAliasesMap:
171221
thisMap =>
172222

173223
override def apply(t: Type) =
174224
if variance <= 0 then t
175225
else t match
176226
case t: CaptureRef if t.isCap =>
177-
Fresh(purpose)
227+
Fresh(origin)
178228
case t @ CapturingType(parent: TypeRef, _) if parent.symbol == defn.Caps_CapSet =>
179229
t
180230
case t @ CapturingType(_, _) =>
@@ -212,23 +262,23 @@ object root:
212262
end CapToFresh
213263

214264
/** Maps cap to fresh */
215-
def capToFresh(tp: Type, purpose: => String)(using Context): Type =
216-
if ccConfig.useSepChecks then CapToFresh(purpose)(tp) else tp
265+
def capToFresh(tp: Type, origin: Origin)(using Context): Type =
266+
if ccConfig.useSepChecks then CapToFresh(origin)(tp) else tp
217267

218268
/** Maps fresh to cap */
219269
def freshToCap(tp: Type)(using Context): Type =
220-
if ccConfig.useSepChecks then CapToFresh("").inverse(tp) else tp
270+
if ccConfig.useSepChecks then CapToFresh(Origin.Unknown).inverse(tp) else tp
221271

222272
/** Map top-level free existential variables one-to-one to Fresh instances */
223-
def resultToFresh(tp: Type, purpose: => String)(using Context): Type =
273+
def resultToFresh(tp: Type, origin: Origin)(using Context): Type =
224274
val subst = new TypeMap:
225275
val seen = EqHashMap[Annotation, CaptureRef]()
226276
var localBinders: SimpleIdentitySet[MethodType] = SimpleIdentitySet.empty
227277

228278
def apply(t: Type): Type = t match
229279
case t @ Result(binder) =>
230280
if localBinders.contains(binder) then t // keep bound references
231-
else seen.getOrElseUpdate(t.annot, Fresh(purpose)) // map free references to Fresh()
281+
else seen.getOrElseUpdate(t.annot, Fresh(origin)) // map free references to Fresh()
232282
case t: MethodType =>
233283
// skip parameters
234284
val saved = localBinders
@@ -294,7 +344,7 @@ object root:
294344
val (k, v) = it.next
295345
if v.annot eq t.annot then ref = k
296346
if ref == null then
297-
ref = Fresh("")
347+
ref = Fresh(Origin.Unknown)
298348
seen(ref) = t
299349
ref
300350
case _ => mapOver(t)

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,11 @@ object Message:
185185
else ref match
186186
case ref @ root.Fresh(hidden) =>
187187
val (kind: root.Kind.Fresh) = ref.rootAnnot.kind: @unchecked
188-
val purpose = kind.purpose()
189-
val descr =
190-
if purpose.startsWith(" in the ") then purpose
191-
else i" created in ${ownerStr(hidden.owner)}$purpose"
188+
val descr = kind.origin match
189+
case origin @ root.Origin.InDecl(sym) if sym.exists =>
190+
origin.explanation
191+
case origin =>
192+
i" created in ${ownerStr(hidden.owner)}${origin.explanation}"
192193
i"a fresh root capability$descr"
193194
case root.Result(binder) => i"a root capability associated with the result type of $binder"
194195
s"$relation $descr"

tests/neg-custom-args/captures/i15116.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
| Found: Foo{val m: (Bar.this.m² : String^)}^{Bar.this.m²}
55
| Required: Foo
66
|
7-
| where: ^ refers to a fresh root capability in the type of value m
7+
| where: ^ refers to a fresh root capability in the type of value m²
88
| m is a value in class Foo
99
| m² is a value in class Bar
1010
|
@@ -22,7 +22,7 @@
2222
| Found: Foo{val m: (Baz.this.m² : String^)}^{Baz.this.m²}
2323
| Required: Foo
2424
|
25-
| where: ^ refers to a fresh root capability in the type of value m
25+
| where: ^ refers to a fresh root capability in the type of value m²
2626
| m is a value in class Foo
2727
| m² is a value in trait Baz
2828
|
@@ -40,7 +40,7 @@
4040
| Found: Foo{val m: (Bar1.this.m² : String^)}^{Bar1.this.m²}
4141
| Required: Foo
4242
|
43-
| where: ^ refers to a fresh root capability in the type of value m
43+
| where: ^ refers to a fresh root capability in the type of value m²
4444
| m is a value in class Foo
4545
| m² is a value in class Bar1
4646
|
@@ -58,7 +58,7 @@
5858
| Found: Foo{val m: (Baz2.this.m² : String^)}^{Baz2.this.m²}
5959
| Required: Foo
6060
|
61-
| where: ^ refers to a fresh root capability in the type of value m
61+
| where: ^ refers to a fresh root capability in the type of value m²
6262
| m is a value in class Foo
6363
| m² is a value in trait Baz2
6464
|

0 commit comments

Comments
 (0)