@@ -2503,7 +2503,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
25032503 assert(pt.typeSymbol == PartialFunctionClass , s " PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt. " )
25042504 val targs = pt.dealiasWiden.typeArgs
25052505
2506- // if targs.head isn't fully defined, we can translate --> error
2506+ // if targs.head isn't fully defined, we can't translate --> error
25072507 targs match {
25082508 case argTp :: _ if isFullyDefined(argTp) => // ok
25092509 case _ => // uh-oh
@@ -2517,9 +2517,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
25172517 // targs must conform to Any for us to synthesize an applyOrElse (fallback to apply otherwise -- typically for @cps annotated targs)
25182518 val targsValidParams = targs forall (_ <:< AnyTpe )
25192519
2520- val anonClass = (context.owner
2521- newAnonymousFunctionClass tree.pos
2522- addAnnotation AnnotationInfo (SerialVersionUIDAttr .tpe, List (Literal (Constant (0 ))), List ()))
2520+ val anonClass = context.owner newAnonymousFunctionClass tree.pos addAnnotation SerialVersionUIDAnnotation
25232521
25242522 import CODE ._
25252523
@@ -2712,17 +2710,197 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
27122710 }
27132711 }
27142712
2713+ /** Synthesize and type check the implementation of a type with a Single Abstract Method
2714+ *
2715+ * `{ (p1: T1, ..., pN: TN) => body } : S`
2716+ *
2717+ * expands to (where `S` is the expected type that defines a single abstract method named `apply`)
2718+ *
2719+ * `{
2720+ * def apply$body(p1: T1, ..., pN: TN): T = body
2721+ * new S {
2722+ * def apply(p1: T1, ..., pN: TN): T = apply$body(p1,..., pN)
2723+ * }
2724+ * }`
2725+ *
2726+ * If 'T' is not fully defined, it is inferred by type checking
2727+ * `apply$body` without a result type before type checking the block.
2728+ * The method's inferred result type is used instead of T`. [See test/files/pos/sammy_poly.scala]
2729+ *
2730+ * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`,
2731+ * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not...
2732+ *
2733+ * The function's body is put in a method outside of the class definition to enforce scoping.
2734+ * S's members should not be in scope in `body`.
2735+ *
2736+ * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list),
2737+ * is largely to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple.
2738+ *
2739+ * NOTE: it would be nicer to not have to type check `apply$body` separately when `T` is not fully defined.
2740+ * However T must be fully defined before we type the instantiation, as it'll end up as a parent type,
2741+ * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code,
2742+ * and have the instantiation of the first occurrence propagate to the rest of the block.
2743+ */
2744+ def synthesizeSAMFunction (sam : Symbol , fun : Function , resPt : Type , samClassTp : Type , mode : Mode ): Tree = {
2745+ // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info
2746+ val sampos = fun.pos
2747+
2748+ // if the expected sam type is fully defined, use it for the method's result type
2749+ // otherwise, NoType, so that type inference will determine the method's result type
2750+ // resPt is syntactically contained in samClassTp, so if the latter is fully defined, so is the former
2751+ // ultimately, we want to fully define samClassTp as it is used as the superclass of our anonymous class
2752+ val samDefTp = if (isFullyDefined(resPt)) resPt else NoType
2753+ val bodyName = newTermName(sam.name + " $body" )
2754+
2755+ // `def '${sam.name}\$body'($p1: $T1, ..., $pN: $TN): $resPt = $body`
2756+ val samBodyDef =
2757+ DefDef (NoMods ,
2758+ bodyName,
2759+ Nil ,
2760+ List (fun.vparams.map(_.duplicate)), // must duplicate as we're also using them for `samDef`
2761+ TypeTree (samDefTp) setPos sampos.focus,
2762+ fun.body)
2763+
2764+ // If we need to enter the sym for the body def before type checking the block,
2765+ // we'll create a nested context, as explained below.
2766+ var nestedTyper = this
2767+
2768+ // Type check body def before classdef to fully determine samClassTp (if necessary).
2769+ // As `samClassTp` determines a parent type for the class,
2770+ // we can't type check `block` in one go unless `samClassTp` is fully defined.
2771+ val samClassTpFullyDefined =
2772+ if (isFullyDefined(samClassTp)) samClassTp
2773+ else try {
2774+ // This creates a symbol for samBodyDef with a type completer that'll be triggered immediately below.
2775+ // The symbol is entered in the same scope used for the block below, and won't thus be reentered later.
2776+ // It has to be a new scope, though, or we'll "get ambiguous reference to overloaded definition" [pos/sammy_twice.scala]
2777+ // makeSilent: [pos/nonlocal-unchecked.scala -- when translation all functions to sams]
2778+ val nestedCtx = enterSym(context.makeNewScope(context.tree, context.owner).makeSilent(), samBodyDef)
2779+ nestedTyper = newTyper(nestedCtx)
2780+
2781+ // NOTE: this `samBodyDef.symbol.info` runs the type completer set up by the enterSym above
2782+ val actualSamType = samBodyDef.symbol.info
2783+
2784+ // we're trying to fully define the type arguments for this type constructor
2785+ val samTyCon = samClassTp.typeSymbol.typeConstructor
2786+
2787+ // the unknowns
2788+ val tparams = samClassTp.typeSymbol.typeParams
2789+ // ... as typevars
2790+ val tvars = tparams map freshVar
2791+
2792+ // 1. Recover partial information:
2793+ // - derive a type from samClassTp that has the corresponding tparams for type arguments that aren't fully defined
2794+ // - constrain typevars to be equal to type args that are fully defined
2795+ val samClassTpMoreDefined = appliedType(samTyCon,
2796+ (samClassTp.typeArgs, tparams, tvars).zipped map {
2797+ case (a, _, tv) if isFullyDefined(a) => tv =:= a; a
2798+ case (_, p, _) => p.typeConstructor
2799+ })
2800+
2801+ // the method type we're expecting the synthesized sam to have, based on the expected sam type,
2802+ // where fully defined type args to samClassTp have been preserved,
2803+ // with the unknown args replaced by their corresponding type param
2804+ val expectedSamType = samClassTpMoreDefined.memberInfo(sam)
2805+
2806+ // 2. make sure the body def's actual type (formals and result) conforms to
2807+ // sam's expected type (in terms of the typevars that represent the sam's class's type params)
2808+ actualSamType <:< expectedSamType.substituteTypes(tparams, tvars)
2809+
2810+ // solve constraints tracked by tvars
2811+ val targs = solvedTypes(tvars, tparams, tparams map varianceInType(sam.info), upper = false , lubDepth(sam.info :: Nil ))
2812+
2813+ debuglog(s " sam infer: $samClassTp --> ${appliedType(samTyCon, targs)} by $actualSamType <:< $expectedSamType --> $targs for $tparams" )
2814+
2815+ // a fully defined samClassTp
2816+ appliedType(samTyCon, targs)
2817+ } catch {
2818+ case _ : NoInstance | _ : TypeError =>
2819+ devWarning(sampos, s " Could not define type $samClassTp using ${samBodyDef.symbol.rawInfo} <:< ${samClassTp memberInfo sam} (for $sam) " )
2820+ samClassTp
2821+ }
2822+
2823+ // `final override def ${sam.name}($p1: $T1, ..., $pN: $TN): $resPt = ${sam.name}\$body'($p1, ..., $pN)`
2824+ val samDef =
2825+ DefDef (Modifiers (FINAL | OVERRIDE | SYNTHETIC ),
2826+ sam.name.toTermName,
2827+ Nil ,
2828+ List (fun.vparams),
2829+ TypeTree (samBodyDef.tpt.tpe) setPos sampos.focus,
2830+ Apply (Ident (bodyName), fun.vparams map (p => Ident (p.name)))
2831+ )
2832+
2833+ val serializableParentAddendum =
2834+ if (typeIsSubTypeOfSerializable(samClassTp)) Nil
2835+ else List (TypeTree (SerializableTpe ))
2836+
2837+ val classDef =
2838+ ClassDef (Modifiers (FINAL ), tpnme.ANON_FUN_NAME , tparams = Nil ,
2839+ gen.mkTemplate(
2840+ parents = TypeTree (samClassTpFullyDefined) :: serializableParentAddendum,
2841+ self = emptyValDef,
2842+ constrMods = NoMods ,
2843+ vparamss = ListOfNil ,
2844+ body = List (samDef),
2845+ superPos = sampos.focus
2846+ )
2847+ )
2848+
2849+ // type checking the whole block, so that everything is packaged together nicely
2850+ // and we don't have to create any symbols by hand
2851+ val block =
2852+ nestedTyper.typedPos(sampos, mode, samClassTpFullyDefined) {
2853+ Block (
2854+ samBodyDef,
2855+ classDef,
2856+ Apply (Select (New (Ident (tpnme.ANON_FUN_NAME )), nme.CONSTRUCTOR ), Nil )
2857+ )
2858+ }
2859+
2860+ classDef.symbol addAnnotation SerialVersionUIDAnnotation
2861+ block
2862+ }
2863+
2864+ /** Type check a function literal.
2865+ *
2866+ * Based on the expected type pt, potentially synthesize an instance of
2867+ * - PartialFunction,
2868+ * - a type with a Single Abstract Method (under -Xexperimental for now).
2869+ */
27152870 private def typedFunction (fun : Function , mode : Mode , pt : Type ): Tree = {
27162871 val numVparams = fun.vparams.length
2717- if (numVparams > definitions.MaxFunctionArity )
2718- return MaxFunctionArityError (fun)
2872+ val FunctionSymbol =
2873+ if (numVparams > definitions.MaxFunctionArity ) NoSymbol
2874+ else FunctionClass (numVparams)
27192875
2720- val FunctionSymbol = FunctionClass (numVparams)
2721- val (argpts, respt) = pt baseType FunctionSymbol match {
2722- case TypeRef (_, FunctionSymbol , args :+ res) => (args, res)
2723- case _ => (fun.vparams map (_ => if (pt == ErrorType ) ErrorType else NoType ), WildcardType )
2724- }
2725- if (argpts.lengthCompare(numVparams) != 0 )
2876+ /* The Single Abstract Member of pt, unless pt is the built-in function type of the expected arity,
2877+ * as `(a => a): Int => Int` should not (yet) get the sam treatment.
2878+ */
2879+ val sam =
2880+ if (! settings.Xexperimental || pt.typeSymbol == FunctionSymbol ) NoSymbol
2881+ else samOf(pt)
2882+
2883+ /* The SAM case comes first so that this works:
2884+ * abstract class MyFun extends (Int => Int)
2885+ * (a => a): MyFun
2886+ *
2887+ * Note that the arity of the sam must correspond to the arity of the function.
2888+ */
2889+ val samViable = sam.exists && sameLength(sam.info.params, fun.vparams)
2890+ val (argpts, respt) =
2891+ if (samViable) {
2892+ val samInfo = pt memberInfo sam
2893+ (samInfo.paramTypes, samInfo.resultType)
2894+ } else {
2895+ pt baseType FunctionSymbol match {
2896+ case TypeRef (_, FunctionSymbol , args :+ res) => (args, res)
2897+ case _ => (fun.vparams map (_ => if (pt == ErrorType ) ErrorType else NoType ), WildcardType )
2898+ }
2899+ }
2900+
2901+ if (! FunctionSymbol .exists)
2902+ MaxFunctionArityError (fun)
2903+ else if (argpts.lengthCompare(numVparams) != 0 )
27262904 WrongNumberOfParametersError (fun, argpts)
27272905 else {
27282906 foreach2(fun.vparams, argpts) { (vparam, argpt) =>
@@ -2733,7 +2911,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
27332911 fun match {
27342912 case etaExpansion(vparams, fn, args) =>
27352913 silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 =>
2736- // if context, undetparams is not empty, the function was polymorphic,
2914+ // if context. undetparams is not empty, the function was polymorphic,
27372915 // so we need the missing arguments to infer its type. See #871
27382916 // println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams)
27392917 val ftpe = normalize(fn1.tpe) baseType FunctionClass (numVparams)
@@ -2761,6 +2939,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
27612939 if (p.tpt.tpe == null ) p.tpt setType outerTyper.typedType(p.tpt).tpe
27622940
27632941 outerTyper.synthesizePartialFunction(p.name, p.pos, fun.body, mode, pt)
2942+
2943+ // Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body`
2944+ // to an instance of the corresponding anonymous subclass of `pt`.
2945+ case _ if samViable =>
2946+ newTyper(context.outer).synthesizeSAMFunction(sam, fun, respt, pt, mode)
2947+
2948+ // regular Function
27642949 case _ =>
27652950 val vparamSyms = fun.vparams map { vparam =>
27662951 enterSym(context, vparam)
0 commit comments