@@ -254,19 +254,36 @@ extends tpd.TreeTraverser:
254
254
val tp1 = mapInferred(tp)
255
255
if boxed then box(tp1) else tp1
256
256
257
- /** Expand some aliases of function types to the underlying functions.
258
- * Right now, these are only $throws aliases, but this could be generalized.
259
- */
260
- private def expandThrowsAlias (tp : Type )(using Context ) = tp match
261
- case AppliedType (tycon, res :: exc :: Nil ) if tycon.typeSymbol == defn.throwsAlias =>
262
- // hard-coded expansion since $throws aliases in stdlib are defined with `?=>` rather than `?->`
263
- defn.FunctionOf (
264
- AnnotatedType (
257
+ /** Recognizer for `res $throws exc`, returning `(res, exc)` in case of success */
258
+ object throwsAlias :
259
+ def unapply (tp : Type )(using Context ): Option [(Type , Type )] = tp match
260
+ case AppliedType (tycon, res :: exc :: Nil ) if tycon.typeSymbol == defn.throwsAlias =>
261
+ Some ((res, exc))
262
+ case _ =>
263
+ None
264
+
265
+ /** Expand $throws aliases. This is hard-coded here since $throws aliases in stdlib
266
+ * are defined with `?=>` rather than `?->`.
267
+ * We also have to add a capture set to the last expanded throws alias. I.e.
268
+ * T $throws E1 $throws E2
269
+ * expands to
270
+ * (erased x$0: CanThrow[E1]) ?-> (erased x$1: CanThrow[E1]) ?->{x$0} T
271
+ */
272
+ private def expandThrowsAlias (tp : Type , encl : List [MethodType ] = Nil )(using Context ): Type = tp match
273
+ case throwsAlias(res, exc) =>
274
+ val paramType = AnnotatedType (
265
275
defn.CanThrowClass .typeRef.appliedTo(exc),
266
- Annotation (defn.ErasedParamAnnot , defn.CanThrowClass .span)) :: Nil ,
267
- res,
268
- isContextual = true
269
- )
276
+ Annotation (defn.ErasedParamAnnot , defn.CanThrowClass .span))
277
+ val isLast = throwsAlias.unapply(res).isEmpty
278
+ val paramName = nme.syntheticParamName(encl.length)
279
+ val mt = ContextualMethodType (paramName :: Nil )(
280
+ _ => paramType :: Nil ,
281
+ mt => if isLast then res else expandThrowsAlias(res, mt :: encl))
282
+ val fntpe = RefinedType (defn.ErasedFunctionClass .typeRef, nme.apply, mt)
283
+ if ! encl.isEmpty && isLast then
284
+ val cs = CaptureSet (encl.map(_.paramRefs.head)* )
285
+ CapturingType (fntpe, cs, boxed = false )
286
+ else fntpe
270
287
case _ => tp
271
288
272
289
private def expandThrowsAliases (using Context ) = new TypeMap :
@@ -283,70 +300,10 @@ extends tpd.TreeTraverser:
283
300
case _ =>
284
301
mapOver(t)
285
302
286
- /** Fill in capture sets of curried function types from left to right, using
287
- * a combination of the following two rules:
288
- *
289
- * 1. Expand `{c} (x: A) -> (y: B) -> C`
290
- * to `{c} (x: A) -> {c} (y: B) -> C`
291
- * 2. Expand `(x: A) -> (y: B) -> C` where `x` is tracked
292
- * to `(x: A) -> {x} (y: B) -> C`
293
- *
294
- * TODO: Should we also propagate capture sets to the left?
295
- */
296
- private def expandAbbreviations (using Context ) = new TypeMap :
297
-
298
- /** Propagate `outerCs` as well as all tracked parameters as capture set to the result type
299
- * of the dependent function type `tp`.
300
- */
301
- def propagateDepFunctionResult (tp : Type , outerCs : CaptureSet ): Type = tp match
302
- case RefinedType (parent, nme.apply, rinfo : MethodType ) =>
303
- val localCs = CaptureSet (rinfo.paramRefs.filter(_.isTracked)* )
304
- val rinfo1 = rinfo.derivedLambdaType(
305
- resType = propagateEnclosing(rinfo.resType, CaptureSet .empty, outerCs ++ localCs))
306
- if rinfo1 ne rinfo then rinfo1.toFunctionType(isJava = false , alwaysDependent = true )
307
- else tp
308
-
309
- /** If `tp` is a function type:
310
- * - add `outerCs` as its capture set,
311
- * - propagate `currentCs`, `outerCs`, and all tracked parameters of `tp` to the right.
312
- */
313
- def propagateEnclosing (tp : Type , currentCs : CaptureSet , outerCs : CaptureSet ): Type = tp match
314
- case tp @ AppliedType (tycon, args) if defn.isFunctionClass(tycon.typeSymbol) =>
315
- val tycon1 = this (tycon)
316
- val args1 = args.init.mapConserve(this )
317
- val tp1 =
318
- if args1.exists(! _.captureSet.isAlwaysEmpty) then
319
- val propagated = propagateDepFunctionResult(
320
- depFun(tycon, args1, args.last), currentCs ++ outerCs)
321
- propagated match
322
- case RefinedType (_, _, mt : MethodType ) =>
323
- if mt.isCaptureDependent then propagated
324
- else
325
- // No need to introduce dependent type, switch back to generic function type
326
- tp.derivedAppliedType(tycon1, args1 :+ mt.resType)
327
- else
328
- val resType1 = propagateEnclosing(
329
- args.last, CaptureSet .empty, currentCs ++ outerCs)
330
- tp.derivedAppliedType(tycon1, args1 :+ resType1)
331
- tp1.capturing(outerCs)
332
- case tp @ RefinedType (parent, nme.apply, rinfo : MethodType ) if defn.isFunctionOrPolyType(tp) =>
333
- propagateDepFunctionResult(mapOver(tp), currentCs ++ outerCs)
334
- .capturing(outerCs)
335
- case _ =>
336
- mapOver(tp)
337
-
338
- def apply (tp : Type ): Type = tp match
339
- case CapturingType (parent, cs) =>
340
- tp.derivedCapturingType(propagateEnclosing(parent, cs, CaptureSet .empty), cs)
341
- case _ =>
342
- propagateEnclosing(tp, CaptureSet .empty, CaptureSet .empty)
343
- end expandAbbreviations
344
-
345
303
private def transformExplicitType (tp : Type , boxed : Boolean )(using Context ): Type =
346
304
val tp1 = expandThrowsAliases(if boxed then box(tp) else tp)
347
305
if tp1 ne tp then capt.println(i " expanded: $tp --> $tp1" )
348
- if ctx.settings.YccNoAbbrev .value then tp1
349
- else expandAbbreviations(tp1)
306
+ tp1
350
307
351
308
/** Transform type of type tree, and remember the transformed type as the type the tree */
352
309
private def transformTT (tree : TypeTree , boxed : Boolean , exact : Boolean )(using Context ): Unit =
0 commit comments