@@ -101,7 +101,7 @@ class Erasure extends Phase with DenotTransformer {
101
101
102
102
def run (implicit ctx : Context ): Unit = {
103
103
val unit = ctx.compilationUnit
104
- unit.tpdTree = eraser.typedExpr(unit.tpdTree)(ctx.fresh.setPhase(this .next))
104
+ unit.tpdTree = eraser.typedExpr(unit.tpdTree)(ctx.fresh.setTyper(eraser). setPhase(this .next))
105
105
}
106
106
107
107
override def checkPostCondition (tree : tpd.Tree )(implicit ctx : Context ): Unit = {
@@ -354,6 +354,106 @@ object Erasure {
354
354
cast(tree, pt)
355
355
end adaptToType
356
356
357
+
358
+ /** The following code:
359
+ *
360
+ * val f: Function1[Int, Any] = x => ...
361
+ *
362
+ * results in the creation of a closure and a method in the typer:
363
+ *
364
+ * def $anonfun(x: Int): Any = ...
365
+ * val f: Function1[Int, Any] = closure($anonfun)
366
+ *
367
+ * Notice that `$anonfun` takes a primitive as argument, but the single abstract method
368
+ * of `Function1` after erasure is:
369
+ *
370
+ * def apply(x: Object): Object
371
+ *
372
+ * which takes a reference as argument. Hence, some form of adaptation is required.
373
+ *
374
+ * If we do nothing, the LambdaMetaFactory bootstrap method will
375
+ * automatically do the adaptation. Unfortunately, the result does not
376
+ * implement the expected Scala semantics: null should be "unboxed" to
377
+ * the default value of the value class, but LMF will throw a
378
+ * NullPointerException instead. LMF is also not capable of doing
379
+ * adaptation for derived value classes.
380
+ *
381
+ * Thus, we need to replace the closure method by a bridge method that
382
+ * forwards to the original closure method with appropriate
383
+ * boxing/unboxing. For our example above, this would be:
384
+ *
385
+ * def $anonfun1(x: Object): Object = $anonfun(BoxesRunTime.unboxToInt(x))
386
+ * val f: Function1 = closure($anonfun1)
387
+ *
388
+ * In general a bridge is needed when, after Erasure, one of the
389
+ * parameter type or the result type of the closure method has a
390
+ * different type, and we cannot rely on auto-adaptation.
391
+ *
392
+ * Auto-adaptation works in the following cases:
393
+ * - If the SAM is replaced by JFunction*mc* in
394
+ * [[FunctionalInterfaces ]], no bridge is needed: the SAM contains
395
+ * default methods to handle adaptation.
396
+ * - If a result type of the closure method is a primitive value type
397
+ * different from Unit, we can rely on the auto-adaptation done by
398
+ * LMF (because it only needs to box, not unbox, so no special
399
+ * handling of null is required).
400
+ * - If the SAM is replaced by JProcedure* in
401
+ * [[DottyBackendInterface ]] (this only happens when no explicit SAM
402
+ * type is given), no bridge is needed to box a Unit result type:
403
+ * the SAM contains a default method to handle that.
404
+ *
405
+ * See test cases lambda-*.scala and t8017/ for concrete examples.
406
+ */
407
+ def adaptClosure (tree : tpd.Closure )(using ctx : Context ): Tree = {
408
+ val implClosure @ Closure (_, meth, _) = tree
409
+
410
+ implClosure.tpe match {
411
+ case SAMType (sam) =>
412
+ val implType = meth.tpe.widen.asInstanceOf [MethodType ]
413
+
414
+ val implParamTypes = implType.paramInfos
415
+ val List (samParamTypes) = sam.paramInfoss
416
+ val implResultType = implType.resultType
417
+ val samResultType = sam.resultType
418
+
419
+ if (! defn.isSpecializableFunction(implClosure.tpe.widen.classSymbol.asClass, implParamTypes, implResultType)) {
420
+ def autoAdaptedParam (tp : Type ) = ! tp.isErasedValueType && ! tp.isPrimitiveValueType
421
+ val explicitSAMType = implClosure.tpt.tpe.exists
422
+ def autoAdaptedResult (tp : Type ) = ! tp.isErasedValueType &&
423
+ (! explicitSAMType || tp.typeSymbol != defn.UnitClass )
424
+ def sameSymbol (tp1 : Type , tp2 : Type ) = tp1.typeSymbol == tp2.typeSymbol
425
+
426
+ val paramAdaptationNeeded =
427
+ implParamTypes.lazyZip(samParamTypes).exists((implType, samType) =>
428
+ ! sameSymbol(implType, samType) && ! autoAdaptedParam(implType))
429
+ val resultAdaptationNeeded =
430
+ ! sameSymbol(implResultType, samResultType) && ! autoAdaptedResult(implResultType)
431
+
432
+ if (paramAdaptationNeeded || resultAdaptationNeeded) {
433
+ val bridgeType =
434
+ if (paramAdaptationNeeded)
435
+ if (resultAdaptationNeeded) sam
436
+ else implType.derivedLambdaType(paramInfos = samParamTypes)
437
+ else implType.derivedLambdaType(resType = samResultType)
438
+ val bridge = ctx.newSymbol(ctx.owner, AdaptedClosureName (meth.symbol.name.asTermName), Flags .Synthetic | Flags .Method , bridgeType)
439
+ val bridgeCtx = ctx.withOwner(bridge)
440
+ Closure (bridge, bridgeParamss => {
441
+ implicit val ctx = bridgeCtx
442
+
443
+ val List (bridgeParams) = bridgeParamss
444
+ assert(ctx.typer.isInstanceOf [Erasure .Typer ])
445
+ val rhs = Apply (meth, bridgeParams.lazyZip(implParamTypes).map(ctx.typer.adapt(_, _)))
446
+ ctx.typer.adapt(rhs, bridgeType.resultType)
447
+ }, targetType = implClosure.tpt.tpe)
448
+ }
449
+ else implClosure
450
+ }
451
+ else implClosure
452
+ case _ =>
453
+ implClosure
454
+ }
455
+ }
456
+
357
457
/** Eta expand given `tree` that has the given method type `mt`, so that
358
458
* it conforms to erased result type `pt`.
359
459
* To do this correctly, we have to look at the tree's original pre-erasure
@@ -392,14 +492,16 @@ object Erasure {
392
492
case refs1 => refs1
393
493
abstracted(args ::: expandedRefs, resTpe, anonFun.info.finalResultType)(
394
494
using ctx.withOwner(anonFun))
395
- Closure (anonFun, lambdaBody)
495
+
496
+ val unadapted = Closure (anonFun, lambdaBody)
497
+ cpy.Block (unadapted)(unadapted.stats, adaptClosure(unadapted.expr.asInstanceOf [Closure ]))
396
498
catch case ex : MatchError =>
397
499
println(i " error while abstracting tree = $tree | mt = $mt | args = $args%, % | tp = $tp | pt = $pt" )
398
500
throw ex
399
501
else
400
502
assert(args.length == targetLength, i " wrong # args tree = $tree | args = $args%, % | mt = $mt | tree type = ${tree.tpe}" )
401
503
val app = untpd.cpy.Apply (tree1)(tree1, args)
402
- assert(ctx.typer.isInstanceOf [Typer ])
504
+ assert(ctx.typer.isInstanceOf [Erasure . Typer ])
403
505
ctx.typer.typed(app, pt)
404
506
.changeOwnerAfter(origOwner, ctx.owner, ctx.erasurePhase.asInstanceOf [Erasure ])
405
507
@@ -765,7 +867,7 @@ object Erasure {
765
867
// accessor that's produced with an `enteredAfter` in ExplicitOuter, so
766
868
// `tranformInfo` of the constructor in erasure yields a method type without
767
869
// an outer parameter. We fix this problem by adding the missing outer
768
- // parameter here.
870
+ // parameter here.
769
871
constr.copySymDenotation(
770
872
info = outer.addParam(constr.owner.asClass, constr.info)
771
873
).installAfter(erasurePhase)
@@ -774,101 +876,9 @@ object Erasure {
774
876
775
877
override def typedClosure (tree : untpd.Closure , pt : Type )(implicit ctx : Context ): Tree = {
776
878
val xxl = defn.isXXLFunctionClass(tree.typeOpt.typeSymbol)
777
- var implClosure @ Closure (_, meth, _) = super .typedClosure(tree, pt)
879
+ var implClosure = super .typedClosure(tree, pt). asInstanceOf [ Closure ]
778
880
if (xxl) implClosure = cpy.Closure (implClosure)(tpt = TypeTree (defn.FunctionXXLClass .typeRef))
779
- implClosure.tpe match {
780
- case SAMType (sam) =>
781
- val implType = meth.tpe.widen.asInstanceOf [MethodType ]
782
-
783
- val implParamTypes = implType.paramInfos
784
- val List (samParamTypes) = sam.paramInfoss
785
- val implResultType = implType.resultType
786
- val samResultType = sam.resultType
787
-
788
- // The following code:
789
- //
790
- // val f: Function1[Int, Any] = x => ...
791
- //
792
- // results in the creation of a closure and a method in the typer:
793
- //
794
- // def $anonfun(x: Int): Any = ...
795
- // val f: Function1[Int, Any] = closure($anonfun)
796
- //
797
- // Notice that `$anonfun` takes a primitive as argument, but the single abstract method
798
- // of `Function1` after erasure is:
799
- //
800
- // def apply(x: Object): Object
801
- //
802
- // which takes a reference as argument. Hence, some form of adaptation is required.
803
- //
804
- // If we do nothing, the LambdaMetaFactory bootstrap method will
805
- // automatically do the adaptation. Unfortunately, the result does not
806
- // implement the expected Scala semantics: null should be "unboxed" to
807
- // the default value of the value class, but LMF will throw a
808
- // NullPointerException instead. LMF is also not capable of doing
809
- // adaptation for derived value classes.
810
- //
811
- // Thus, we need to replace the closure method by a bridge method that
812
- // forwards to the original closure method with appropriate
813
- // boxing/unboxing. For our example above, this would be:
814
- //
815
- // def $anonfun1(x: Object): Object = $anonfun(BoxesRunTime.unboxToInt(x))
816
- // val f: Function1 = closure($anonfun1)
817
- //
818
- // In general a bridge is needed when, after Erasure, one of the
819
- // parameter type or the result type of the closure method has a
820
- // different type, and we cannot rely on auto-adaptation.
821
- //
822
- // Auto-adaptation works in the following cases:
823
- // - If the SAM is replaced by JFunction*mc* in
824
- // [[FunctionalInterfaces]], no bridge is needed: the SAM contains
825
- // default methods to handle adaptation.
826
- // - If a result type of the closure method is a primitive value type
827
- // different from Unit, we can rely on the auto-adaptation done by
828
- // LMF (because it only needs to box, not unbox, so no special
829
- // handling of null is required).
830
- // - If the SAM is replaced by JProcedure* in
831
- // [[DottyBackendInterface]] (this only happens when no explicit SAM
832
- // type is given), no bridge is needed to box a Unit result type:
833
- // the SAM contains a default method to handle that.
834
- //
835
- // See test cases lambda-*.scala and t8017/ for concrete examples.
836
-
837
- if (! defn.isSpecializableFunction(implClosure.tpe.widen.classSymbol.asClass, implParamTypes, implResultType)) {
838
- def autoAdaptedParam (tp : Type ) = ! tp.isErasedValueType && ! tp.isPrimitiveValueType
839
- val explicitSAMType = implClosure.tpt.tpe.exists
840
- def autoAdaptedResult (tp : Type ) = ! tp.isErasedValueType &&
841
- (! explicitSAMType || tp.typeSymbol != defn.UnitClass )
842
- def sameSymbol (tp1 : Type , tp2 : Type ) = tp1.typeSymbol == tp2.typeSymbol
843
-
844
- val paramAdaptationNeeded =
845
- implParamTypes.lazyZip(samParamTypes).exists((implType, samType) =>
846
- ! sameSymbol(implType, samType) && ! autoAdaptedParam(implType))
847
- val resultAdaptationNeeded =
848
- ! sameSymbol(implResultType, samResultType) && ! autoAdaptedResult(implResultType)
849
-
850
- if (paramAdaptationNeeded || resultAdaptationNeeded) {
851
- val bridgeType =
852
- if (paramAdaptationNeeded)
853
- if (resultAdaptationNeeded) sam
854
- else implType.derivedLambdaType(paramInfos = samParamTypes)
855
- else implType.derivedLambdaType(resType = samResultType)
856
- val bridge = ctx.newSymbol(ctx.owner, AdaptedClosureName (meth.symbol.name.asTermName), Flags .Synthetic | Flags .Method , bridgeType)
857
- val bridgeCtx = ctx.withOwner(bridge)
858
- Closure (bridge, bridgeParamss => {
859
- implicit val ctx = bridgeCtx
860
-
861
- val List (bridgeParams) = bridgeParamss
862
- val rhs = Apply (meth, bridgeParams.lazyZip(implParamTypes).map(adapt(_, _)))
863
- adapt(rhs, bridgeType.resultType)
864
- }, targetType = implClosure.tpt.tpe)
865
- }
866
- else implClosure
867
- }
868
- else implClosure
869
- case _ =>
870
- implClosure
871
- }
881
+ adaptClosure(implClosure)
872
882
}
873
883
874
884
override def typedTypeDef (tdef : untpd.TypeDef , sym : Symbol )(implicit ctx : Context ): Tree =
0 commit comments