@@ -741,6 +741,26 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
741741 case _ =>
742742 }
743743
744+ /**
745+ * Convert a SAM type to the corresponding FunctionType,
746+ * extrapolating BoundedWildcardTypes in the process
747+ * (no type precision is lost by the extrapolation,
748+ * but this facilitates dealing with the types arising from Java's use-site variance).
749+ */
750+ def samToFunctionType (tp : Type , sam : Symbol = NoSymbol ): Type = {
751+ val samSym = sam orElse samOf(tp)
752+
753+ def correspondingFunctionSymbol = {
754+ val numVparams = samSym.info.params.length
755+ if (numVparams > definitions.MaxFunctionArity ) NoSymbol
756+ else FunctionClass (numVparams)
757+ }
758+
759+ if (samSym.exists && samSym.owner != correspondingFunctionSymbol) // don't treat Functions as SAMs
760+ wildcardExtrapolation(normalize(tp memberInfo samSym))
761+ else NoType
762+ }
763+
744764 /** Perform the following adaptations of expression, pattern or type `tree` wrt to
745765 * given mode `mode` and given prototype `pt`:
746766 * (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
@@ -824,7 +844,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
824844 case Block (_, tree1) => tree1.symbol
825845 case _ => tree.symbol
826846 }
827- if (! meth.isConstructor && isFunctionType(pt)) { // (4.2)
847+ if (! meth.isConstructor && ( isFunctionType(pt) || samOf(pt).exists )) { // (4.2)
828848 debuglog(s " eta-expanding $tree: ${tree.tpe} to $pt" )
829849 checkParamsConvertible(tree, tree.tpe)
830850 val tree0 = etaExpand(context.unit, tree, this )
@@ -2681,7 +2701,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
26812701 * `{
26822702 * def apply$body(p1: T1, ..., pN: TN): T = body
26832703 * new S {
2684- * def apply(p1: T1, ..., pN: TN): T = apply$body(p1,..., pN)
2704+ * def apply(p1: T1' , ..., pN: TN' ): T' = apply$body(p1,..., pN)
26852705 * }
26862706 * }`
26872707 *
@@ -2691,6 +2711,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
26912711 *
26922712 * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`,
26932713 * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not...
2714+ * If it is not fully defined, we derive `samClassTpFullyDefined` by inferring any unknown type parameters.
2715+ *
2716+ * The types T1' ... TN' and T' are derived from the method signature of the sam method,
2717+ * as seen from the fully defined `samClassTpFullyDefined`.
26942718 *
26952719 * The function's body is put in a method outside of the class definition to enforce scoping.
26962720 * S's members should not be in scope in `body`.
@@ -2702,6 +2726,22 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
27022726 * However T must be fully defined before we type the instantiation, as it'll end up as a parent type,
27032727 * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code,
27042728 * and have the instantiation of the first occurrence propagate to the rest of the block.
2729+ *
2730+ * TODO: by-name params
2731+ * scala> trait LazySink { def accept(a: => Any): Unit }
2732+ * defined trait LazySink
2733+ *
2734+ * scala> val f: LazySink = (a) => (a, a)
2735+ * f: LazySink = $anonfun$1@1fb26910
2736+ *
2737+ * scala> f(println("!"))
2738+ * <console>:10: error: LazySink does not take parameters
2739+ * f(println("!"))
2740+ * ^
2741+ *
2742+ * scala> f.accept(println("!"))
2743+ * !
2744+ * !
27052745 */
27062746 def synthesizeSAMFunction (sam : Symbol , fun : Function , resPt : Type , samClassTp : Type , mode : Mode ): Tree = {
27072747 // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info
@@ -2782,14 +2822,21 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
27822822 samClassTp
27832823 }
27842824
2785- // `final override def ${sam.name}($p1: $T1, ..., $pN: $TN): $resPt = ${sam.name}\$body'($p1, ..., $pN)`
2825+ // what's the signature of the method that we should actually be overriding?
2826+ val samMethTp = samClassTpFullyDefined memberInfo sam
2827+ // Before the mutation, `tp <:< vpar.tpt.tpe` should hold.
2828+ // TODO: error message when this is not the case, as the expansion won't type check
2829+ // - Ti' <:< Ti and T <: T' must hold for the samDef body to type check
2830+ val funArgTps = foreach2(samMethTp.paramTypes, fun.vparams)((tp, vpar) => vpar.tpt setType tp)
2831+
2832+ // `final override def ${sam.name}($p1: $T1', ..., $pN: $TN'): ${samMethTp.finalResultType} = ${sam.name}\$body'($p1, ..., $pN)`
27862833 val samDef =
27872834 DefDef (Modifiers (FINAL | OVERRIDE | SYNTHETIC ),
27882835 sam.name.toTermName,
27892836 Nil ,
27902837 List (fun.vparams),
2791- TypeTree (samBodyDef.tpt.tpe ) setPos sampos.focus,
2792- Apply (Ident (bodyName), fun.vparams map (p => Ident (p.name)) )
2838+ TypeTree (samMethTp.finalResultType ) setPos sampos.focus,
2839+ Apply (Ident (bodyName), fun.vparams map gen.paramToArg )
27932840 )
27942841
27952842 val serializableParentAddendum =
@@ -2819,6 +2866,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
28192866 )
28202867 }
28212868
2869+ // TODO: improve error reporting -- when we're in silent mode (from `silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError`)
2870+ // the errors in the function don't get out...
2871+ if (block exists (_.isErroneous))
2872+ context.error(fun.pos, s " Could not derive subclass of $samClassTp\n (with SAM `def $sam$samMethTp`) \n based on: $fun. " )
2873+
28222874 classDef.symbol addAnnotation SerialVersionUIDAnnotation
28232875 block
28242876 }
@@ -2839,7 +2891,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
28392891 * as `(a => a): Int => Int` should not (yet) get the sam treatment.
28402892 */
28412893 val sam =
2842- if (! settings. Xexperimental || pt.typeSymbol == FunctionSymbol ) NoSymbol
2894+ if (pt.typeSymbol == FunctionSymbol ) NoSymbol
28432895 else samOf(pt)
28442896
28452897 /* The SAM case comes first so that this works:
@@ -2849,15 +2901,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
28492901 * Note that the arity of the sam must correspond to the arity of the function.
28502902 */
28512903 val samViable = sam.exists && sameLength(sam.info.params, fun.vparams)
2904+ val ptNorm = if (samViable) samToFunctionType(pt, sam) else pt
28522905 val (argpts, respt) =
2853- if (samViable) {
2854- val samInfo = pt memberInfo sam
2855- (samInfo.paramTypes, samInfo.resultType)
2856- } else {
2857- pt baseType FunctionSymbol match {
2858- case TypeRef (_, FunctionSymbol , args :+ res) => (args, res)
2859- case _ => (fun.vparams map (_ => if (pt == ErrorType ) ErrorType else NoType ), WildcardType )
2860- }
2906+ ptNorm baseType FunctionSymbol match {
2907+ case TypeRef (_, FunctionSymbol , args :+ res) => (args, res)
2908+ case _ => (fun.vparams map (_ => if (pt == ErrorType ) ErrorType else NoType ), WildcardType )
28612909 }
28622910
28632911 if (! FunctionSymbol .exists)
0 commit comments