Skip to content

Commit 3c95134

Browse files
authored
Simplify cc/Synthetics.scala (#18273)
Refactoring to avoid some duplication.
2 parents 7b39cb6 + 5b66901 commit 3c95134

File tree

2 files changed

+148
-170
lines changed

2 files changed

+148
-170
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ object CheckCaptures:
3838
if sym.isAllOf(PrivateParamAccessor) && !sym.hasAnnotation(defn.ConstructorOnlyAnnot) then
3939
sym.copySymDenotation(initFlags = sym.flags &~ Private | Recheck.ResetPrivate)
4040
else if Synthetics.needsTransform(sym) then
41-
Synthetics.transformToCC(sym)
41+
Synthetics.transform(sym, toCC = true)
4242
else
4343
sym
4444
end Pre
@@ -173,7 +173,7 @@ class CheckCaptures extends Recheck, SymTransformer:
173173
super.run
174174

175175
override def transformSym(sym: SymDenotation)(using Context): SymDenotation =
176-
if Synthetics.needsTransform(sym) then Synthetics.transformFromCC(sym)
176+
if Synthetics.needsTransform(sym) then Synthetics.transform(sym, toCC = false)
177177
else super.transformSym(sym)
178178

179179
class CaptureChecker(ictx: Context) extends Rechecker(ictx):

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

Lines changed: 146 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -56,177 +56,155 @@ object Synthetics:
5656
|| isSyntheticCompanionMethod(sym, nme.fromProduct)
5757
|| needsTransform(sym))
5858

59-
/** Add capture dependencies to the type of the `apply` or `copy` method of a case class.
60-
* An apply method in a case class like this:
61-
* case class CC(a: {d} A, b: B, {cap} c: C)
62-
* would get type
63-
* def apply(a': {d} A, b: B, {cap} c': C): {a', c'} CC { val a = {a'} A, val c = {c'} C }
64-
* where `'` is used to indicate the difference between parameter symbol and refinement name.
65-
* Analogous for the copy method.
59+
/** Transform the type of a method either to its type under capture checking
60+
* or back to its previous type.
61+
* @param sym The method to transform @pre needsTransform(sym) must hold.
62+
* @param toCC Whether to transform the type to capture checking or back.
6663
*/
67-
private def addCaptureDeps(info: Type)(using Context): Type = info match
68-
case info: MethodType =>
69-
val trackedParams = info.paramRefs.filter(atPhase(checkCapturesPhase)(_.isTracked))
70-
def augmentResult(tp: Type): Type = tp match
71-
case tp: MethodOrPoly =>
72-
tp.derivedLambdaType(resType = augmentResult(tp.resType))
73-
case _ =>
74-
val refined = trackedParams.foldLeft(tp) { (parent, pref) =>
75-
RefinedType(parent, pref.paramName,
76-
CapturingType(
77-
atPhase(ctx.phase.next)(pref.underlying.stripCapturing),
78-
CaptureSet(pref)))
79-
}
80-
CapturingType(refined, CaptureSet(trackedParams*))
81-
if trackedParams.isEmpty then info
82-
else augmentResult(info).showing(i"augment apply/copy type $info to $result", capt)
83-
case info: PolyType =>
84-
info.derivedLambdaType(resType = addCaptureDeps(info.resType))
85-
case _ =>
86-
info
87-
88-
/** Drop capture dependencies from the type of `apply` or `copy` method of a case class */
89-
private def dropCaptureDeps(tp: Type)(using Context): Type = tp match
90-
case tp: MethodOrPoly =>
91-
tp.derivedLambdaType(resType = dropCaptureDeps(tp.resType))
92-
case CapturingType(parent, _) =>
93-
dropCaptureDeps(parent)
94-
case RefinedType(parent, _, _) =>
95-
dropCaptureDeps(parent)
96-
case _ =>
97-
tp
98-
99-
/** Add capture information to the type of the default getter of a case class copy method */
100-
private def addDefaultGetterCapture(info: Type, owner: Symbol, idx: Int)(using Context): Type = info match
101-
case info: MethodOrPoly =>
102-
info.derivedLambdaType(resType = addDefaultGetterCapture(info.resType, owner, idx))
103-
case info: ExprType =>
104-
info.derivedExprType(addDefaultGetterCapture(info.resType, owner, idx))
105-
case EventuallyCapturingType(parent, _) =>
106-
addDefaultGetterCapture(parent, owner, idx)
107-
case info @ AnnotatedType(parent, annot) =>
108-
info.derivedAnnotatedType(addDefaultGetterCapture(parent, owner, idx), annot)
109-
case _ if idx < owner.asClass.paramGetters.length =>
110-
val param = owner.asClass.paramGetters(idx)
111-
val pinfo = param.info
112-
atPhase(ctx.phase.next) {
113-
if pinfo.captureSet.isAlwaysEmpty then info
114-
else CapturingType(pinfo.stripCapturing, CaptureSet(param.termRef))
115-
}
116-
case _ =>
117-
info
118-
119-
/** Drop capture information from the type of the default getter of a case class copy method */
120-
private def dropDefaultGetterCapture(info: Type)(using Context): Type = info match
121-
case info: MethodOrPoly =>
122-
info.derivedLambdaType(resType = dropDefaultGetterCapture(info.resType))
123-
case CapturingType(parent, _) =>
124-
parent
125-
case info @ AnnotatedType(parent, annot) =>
126-
info.derivedAnnotatedType(dropDefaultGetterCapture(parent), annot)
127-
case _ =>
128-
info
129-
130-
/** Augment an unapply of type `(x: C): D` to `(x: {cap} C): {x} D` */
131-
private def addUnapplyCaptures(info: Type)(using Context): Type = info match
132-
case info: MethodType =>
133-
val paramInfo :: Nil = info.paramInfos: @unchecked
134-
val newParamInfo =
135-
CapturingType(paramInfo, CaptureSet.universal)
136-
val trackedParam = info.paramRefs.head
137-
def newResult(tp: Type): Type = tp match
138-
case tp: MethodOrPoly =>
139-
tp.derivedLambdaType(resType = newResult(tp.resType))
140-
case _ =>
141-
CapturingType(tp, CaptureSet(trackedParam))
142-
info.derivedLambdaType(paramInfos = newParamInfo :: Nil, resType = newResult(info.resType))
143-
.showing(i"augment unapply type $info to $result", capt)
144-
case info: PolyType =>
145-
info.derivedLambdaType(resType = addUnapplyCaptures(info.resType))
146-
147-
/** Drop added capture information from the type of an `unapply` */
148-
private def dropUnapplyCaptures(info: Type)(using Context): Type = info match
149-
case info: MethodType =>
150-
info.paramInfos match
151-
case CapturingType(oldParamInfo, _) :: Nil =>
152-
def oldResult(tp: Type): Type = tp match
64+
def transform(sym: SymDenotation, toCC: Boolean)(using Context): SymDenotation =
65+
66+
/** Add capture dependencies to the type of the `apply` or `copy` method of a case class.
67+
* An apply method in a case class like this:
68+
* case class CC(a: A^{d}, b: B, c: C^{cap})
69+
* would get type
70+
* def apply(a': A^{d}, b: B, c': C^{cap}): CC^{a', c'} { val a = A^{a'}, val c = C^{c'} }
71+
* where `'` is used to indicate the difference between parameter symbol and refinement name.
72+
* Analogous for the copy method.
73+
*/
74+
def addCaptureDeps(info: Type): Type = info match
75+
case info: MethodType =>
76+
val trackedParams = info.paramRefs.filter(atPhase(checkCapturesPhase)(_.isTracked))
77+
def augmentResult(tp: Type): Type = tp match
78+
case tp: MethodOrPoly =>
79+
tp.derivedLambdaType(resType = augmentResult(tp.resType))
80+
case _ =>
81+
val refined = trackedParams.foldLeft(tp) { (parent, pref) =>
82+
RefinedType(parent, pref.paramName,
83+
CapturingType(
84+
atPhase(ctx.phase.next)(pref.underlying.stripCapturing),
85+
CaptureSet(pref)))
86+
}
87+
CapturingType(refined, CaptureSet(trackedParams*))
88+
if trackedParams.isEmpty then info
89+
else augmentResult(info).showing(i"augment apply/copy type $info to $result", capt)
90+
case info: PolyType =>
91+
info.derivedLambdaType(resType = addCaptureDeps(info.resType))
92+
case _ =>
93+
info
94+
95+
/** Drop capture dependencies from the type of `apply` or `copy` method of a case class */
96+
def dropCaptureDeps(tp: Type): Type = tp match
97+
case tp: MethodOrPoly =>
98+
tp.derivedLambdaType(resType = dropCaptureDeps(tp.resType))
99+
case CapturingType(parent, _) =>
100+
dropCaptureDeps(parent)
101+
case RefinedType(parent, _, _) =>
102+
dropCaptureDeps(parent)
103+
case _ =>
104+
tp
105+
106+
/** Add capture information to the type of the default getter of a case class copy method
107+
* if toCC = true, or remove the added info again if toCC = false.
108+
*/
109+
def transformDefaultGetterCaptures(info: Type, owner: Symbol, idx: Int)(using Context): Type = info match
110+
case info: MethodOrPoly =>
111+
info.derivedLambdaType(resType = transformDefaultGetterCaptures(info.resType, owner, idx))
112+
case info: ExprType =>
113+
info.derivedExprType(transformDefaultGetterCaptures(info.resType, owner, idx))
114+
case EventuallyCapturingType(parent, _) =>
115+
if toCC then transformDefaultGetterCaptures(parent, owner, idx)
116+
else parent
117+
case info @ AnnotatedType(parent, annot) =>
118+
info.derivedAnnotatedType(transformDefaultGetterCaptures(parent, owner, idx), annot)
119+
case _ if toCC && idx < owner.asClass.paramGetters.length =>
120+
val param = owner.asClass.paramGetters(idx)
121+
val pinfo = param.info
122+
atPhase(ctx.phase.next) {
123+
if pinfo.captureSet.isAlwaysEmpty then info
124+
else CapturingType(pinfo.stripCapturing, CaptureSet(param.termRef))
125+
}
126+
case _ =>
127+
info
128+
129+
/** Augment an unapply of type `(x: C): D` to `(x: C^{cap}): D^{x}` if toCC is true,
130+
* or remove the added capture sets again if toCC = false.
131+
*/
132+
def transformUnapplyCaptures(info: Type)(using Context): Type = info match
133+
case info: MethodType =>
134+
if toCC then
135+
val paramInfo :: Nil = info.paramInfos: @unchecked
136+
val newParamInfo = CapturingType(paramInfo, CaptureSet.universal)
137+
val trackedParam = info.paramRefs.head
138+
def newResult(tp: Type): Type = tp match
153139
case tp: MethodOrPoly =>
154-
tp.derivedLambdaType(resType = oldResult(tp.resType))
155-
case CapturingType(tp, _) =>
156-
tp
157-
info.derivedLambdaType(paramInfos = oldParamInfo :: Nil, resType = oldResult(info.resType))
158-
case _ =>
159-
info
160-
case info: PolyType =>
161-
info.derivedLambdaType(resType = dropUnapplyCaptures(info.resType))
162-
163-
private def transformComposeCaptures(symd: SymDenotation, toCC: Boolean)(using Context): Type =
164-
val (pt: PolyType) = symd.info: @unchecked
165-
val (mt: MethodType) = pt.resType: @unchecked
166-
val (enclThis: ThisType) = symd.owner.thisType: @unchecked
167-
val mt1 =
140+
tp.derivedLambdaType(resType = newResult(tp.resType))
141+
case _ =>
142+
CapturingType(tp, CaptureSet(trackedParam))
143+
info.derivedLambdaType(paramInfos = newParamInfo :: Nil, resType = newResult(info.resType))
144+
.showing(i"augment unapply type $info to $result", capt)
145+
else info.paramInfos match
146+
case CapturingType(oldParamInfo, _) :: Nil =>
147+
def oldResult(tp: Type): Type = tp match
148+
case tp: MethodOrPoly =>
149+
tp.derivedLambdaType(resType = oldResult(tp.resType))
150+
case CapturingType(tp, _) =>
151+
tp
152+
info.derivedLambdaType(paramInfos = oldParamInfo :: Nil, resType = oldResult(info.resType))
153+
case _ =>
154+
info
155+
case info: PolyType =>
156+
info.derivedLambdaType(resType = transformUnapplyCaptures(info.resType))
157+
158+
def transformComposeCaptures(symd: SymDenotation) =
159+
val (pt: PolyType) = symd.info: @unchecked
160+
val (mt: MethodType) = pt.resType: @unchecked
161+
val (enclThis: ThisType) = symd.owner.thisType: @unchecked
162+
val mt1 =
163+
if toCC then
164+
MethodType(mt.paramNames)(
165+
mt1 => mt.paramInfos.map(_.capturing(CaptureSet.universal)),
166+
mt1 => CapturingType(mt.resType, CaptureSet(enclThis, mt1.paramRefs.head)))
167+
else
168+
MethodType(mt.paramNames)(
169+
mt1 => mt.paramInfos.map(_.stripCapturing),
170+
mt1 => mt.resType.stripCapturing)
171+
pt.derivedLambdaType(resType = mt1)
172+
173+
def transformCurriedTupledCaptures(symd: SymDenotation) =
174+
val (et: ExprType) = symd.info: @unchecked
175+
val (enclThis: ThisType) = symd.owner.thisType: @unchecked
176+
def mapFinalResult(tp: Type, f: Type => Type): Type =
177+
val defn.FunctionOf(args, res, isContextual) = tp: @unchecked
178+
if defn.isFunctionNType(res) then
179+
defn.FunctionOf(args, mapFinalResult(res, f), isContextual)
180+
else
181+
f(tp)
182+
val resType1 =
183+
if toCC then
184+
mapFinalResult(et.resType, CapturingType(_, CaptureSet(enclThis)))
185+
else
186+
et.resType.stripCapturing
187+
ExprType(resType1)
188+
189+
def transformCompareCaptures =
168190
if toCC then
169-
MethodType(mt.paramNames)(
170-
mt1 => mt.paramInfos.map(_.capturing(CaptureSet.universal)),
171-
mt1 => CapturingType(mt.resType, CaptureSet(enclThis, mt1.paramRefs.head)))
191+
MethodType(defn.ObjectType.capturing(CaptureSet.universal) :: Nil, defn.BooleanType)
172192
else
173-
MethodType(mt.paramNames)(
174-
mt1 => mt.paramInfos.map(_.stripCapturing),
175-
mt1 => mt.resType.stripCapturing)
176-
pt.derivedLambdaType(resType = mt1)
177-
178-
def transformCurriedTupledCaptures(symd: SymDenotation, toCC: Boolean)(using Context): Type =
179-
val (et: ExprType) = symd.info: @unchecked
180-
val (enclThis: ThisType) = symd.owner.thisType: @unchecked
181-
def mapFinalResult(tp: Type, f: Type => Type): Type =
182-
val defn.FunctionOf(args, res, isContextual) = tp: @unchecked
183-
if defn.isFunctionNType(res) then
184-
defn.FunctionOf(args, mapFinalResult(res, f), isContextual)
185-
else
186-
f(tp)
187-
val resType1 =
188-
if toCC then
189-
mapFinalResult(et.resType, CapturingType(_, CaptureSet(enclThis)))
190-
else
191-
et.resType.stripCapturing
192-
ExprType(resType1)
193-
194-
/** If `sym` refers to a synthetic apply, unapply, copy, or copy default getter method
195-
* of a case class, transform it to account for capture information.
196-
* The method is run in phase CheckCaptures.Pre
197-
* @pre needsTransform(sym)
198-
*/
199-
def transformToCC(sym: SymDenotation)(using Context): SymDenotation = sym.name match
200-
case DefaultGetterName(nme.copy, n) =>
201-
sym.copySymDenotation(info = addDefaultGetterCapture(sym.info, sym.owner, n))
202-
case nme.unapply =>
203-
sym.copySymDenotation(info = addUnapplyCaptures(sym.info))
204-
case nme.apply | nme.copy =>
205-
sym.copySymDenotation(info = addCaptureDeps(sym.info))
206-
case nme.andThen | nme.compose =>
207-
sym.copySymDenotation(info = transformComposeCaptures(sym, toCC = true))
208-
case nme.curried | nme.tupled =>
209-
sym.copySymDenotation(info = transformCurriedTupledCaptures(sym, toCC = true))
210-
case n if n == nme.eq || n == nme.ne =>
211-
sym.copySymDenotation(info =
212-
MethodType(defn.ObjectType.capturing(CaptureSet.universal) :: Nil, defn.BooleanType))
213-
214-
/** If `sym` refers to a synthetic apply, unapply, copy, or copy default getter method
215-
* of a case class, transform it back to what it was before the CC phase.
216-
* @pre needsTransform(sym)
217-
*/
218-
def transformFromCC(sym: SymDenotation)(using Context): SymDenotation = sym.name match
219-
case DefaultGetterName(nme.copy, n) =>
220-
sym.copySymDenotation(info = dropDefaultGetterCapture(sym.info))
221-
case nme.unapply =>
222-
sym.copySymDenotation(info = dropUnapplyCaptures(sym.info))
223-
case nme.apply | nme.copy =>
224-
sym.copySymDenotation(info = dropCaptureDeps(sym.info))
225-
case nme.andThen | nme.compose =>
226-
sym.copySymDenotation(info = transformComposeCaptures(sym, toCC = false))
227-
case nme.curried | nme.tupled =>
228-
sym.copySymDenotation(info = transformCurriedTupledCaptures(sym, toCC = false))
229-
case n if n == nme.eq || n == nme.ne =>
230-
sym.copySymDenotation(info = defn.methOfAnyRef(defn.BooleanType))
193+
defn.methOfAnyRef(defn.BooleanType)
194+
195+
sym.copySymDenotation(info = sym.name match
196+
case DefaultGetterName(nme.copy, n) =>
197+
transformDefaultGetterCaptures(sym.info, sym.owner, n)
198+
case nme.unapply =>
199+
transformUnapplyCaptures(sym.info)
200+
case nme.apply | nme.copy =>
201+
if toCC then addCaptureDeps(sym.info) else dropCaptureDeps(sym.info)
202+
case nme.andThen | nme.compose =>
203+
transformComposeCaptures(sym)
204+
case nme.curried | nme.tupled =>
205+
transformCurriedTupledCaptures(sym)
206+
case n if n == nme.eq || n == nme.ne =>
207+
transformCompareCaptures)
208+
end transform
231209

232210
end Synthetics

0 commit comments

Comments
 (0)