diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 54 | ||||
-rw-r--r-- | test/files/pos/sammy_poly.flags | 1 | ||||
-rw-r--r-- | test/files/pos/sammy_poly.scala | 6 | ||||
-rw-r--r-- | test/files/pos/sammy_scope.flags | 1 | ||||
-rw-r--r-- | test/files/pos/sammy_scope.scala | 8 | ||||
-rw-r--r-- | test/files/pos/sammy_twice.flags | 1 | ||||
-rw-r--r-- | test/files/pos/sammy_twice.scala | 9 | ||||
-rw-r--r-- | test/files/pos/t6221.scala | 8 |
8 files changed, 76 insertions, 12 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 79039e3436..dfd962e13e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2846,18 +2846,45 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper block } - + /** Type check a function literal. + * + * Based on the expected type pt, potentially synthesize an instance of + * - PartialFunction, + * - a type with a Single Abstract Method (under -Xexperimental for now). + */ private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = { val numVparams = fun.vparams.length - if (numVparams > definitions.MaxFunctionArity) - return MaxFunctionArityError(fun) + val FunctionSymbol = + if (numVparams > definitions.MaxFunctionArity) NoSymbol + else FunctionClass(numVparams) - val FunctionSymbol = FunctionClass(numVparams) - val (argpts, respt) = pt baseType FunctionSymbol match { - case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) - case _ => (fun.vparams map (_ => NoType), WildcardType) - } - if (argpts.lengthCompare(numVparams) != 0) + /* The Single Abstract Member of pt, unless pt is the built-in function type of the expected arity, + * as `(a => a): Int => Int` should not (yet) get the sam treatment. + */ + val sam = + if (!settings.Xexperimental || pt.typeSymbol == FunctionSymbol) NoSymbol + else samOf(pt) + + /* The SAM case comes first so that this works: + * abstract class MyFun extends (Int => Int) + * (a => a): MyFun + * + * Note that the arity of the sam must correspond to the arity of the function. + */ + val (argpts, respt) = + if (sam.exists && sameLength(sam.info.params, fun.vparams)) { + val samInfo = pt memberInfo sam + (samInfo.paramTypes, samInfo.resultType) + } else { + pt baseType FunctionSymbol match { + case TypeRef(_, FunctionSymbol, args :+ res) => (args, res) + case _ => (fun.vparams map (_ => NoType), WildcardType) + } + } + + if (!FunctionSymbol.exists) + MaxFunctionArityError(fun) + else if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts) else { foreach2(fun.vparams, argpts) { (vparam, argpt) => @@ -2868,7 +2895,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper fun match { case etaExpansion(vparams, fn, args) => silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 => - // if context,undetparams is not empty, the function was polymorphic, + // if context.undetparams is not empty, the function was polymorphic, // so we need the missing arguments to infer its type. See #871 //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams) val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams) @@ -2896,6 +2923,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe outerTyper.synthesizePartialFunction(p.name, p.pos, fun.body, mode, pt) + + // Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body` + // to an instance of the corresponding anonymous subclass of `pt`. + case _ if sam.exists => + newTyper(context.outer).synthesizeSAMFunction(sam, fun, respt, pt, mode) + + // regular Function case _ => val vparamSyms = fun.vparams map { vparam => enterSym(context, vparam) diff --git a/test/files/pos/sammy_poly.flags b/test/files/pos/sammy_poly.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_poly.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_poly.scala b/test/files/pos/sammy_poly.scala new file mode 100644 index 0000000000..f03be4f8f5 --- /dev/null +++ b/test/files/pos/sammy_poly.scala @@ -0,0 +1,6 @@ +// test synthesizeSAMFunction where the sam type is not fully defined +class T { + trait F[T, U] { def apply(x: T): U } + def app[T, U](x: T)(f: F[T, U]): U = f(x) + app(1)(x => List(x)) +}
\ No newline at end of file diff --git a/test/files/pos/sammy_scope.flags b/test/files/pos/sammy_scope.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_scope.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_scope.scala b/test/files/pos/sammy_scope.scala new file mode 100644 index 0000000000..8f1fe7058e --- /dev/null +++ b/test/files/pos/sammy_scope.scala @@ -0,0 +1,8 @@ +// test synthesizeSAMFunction: scope hygiene +abstract class SamFun[T1, R] { self => + def apply(v1: T1): R + + // this should type check, as the apply ref is equivalent to self.apply + // it shouldn't resolve to the sam's apply that's synthesized (that wouldn't type check, hence the pos test) + def compose[A](g: SamFun[A, T1]): SamFun[A, R] = { x => apply(g(x)) } +}
\ No newline at end of file diff --git a/test/files/pos/sammy_twice.flags b/test/files/pos/sammy_twice.flags new file mode 100644 index 0000000000..48fd867160 --- /dev/null +++ b/test/files/pos/sammy_twice.flags @@ -0,0 +1 @@ +-Xexperimental diff --git a/test/files/pos/sammy_twice.scala b/test/files/pos/sammy_twice.scala new file mode 100644 index 0000000000..c91f5b9fd2 --- /dev/null +++ b/test/files/pos/sammy_twice.scala @@ -0,0 +1,9 @@ +// test repeated synthesizeSAMFunction where the sam type is not fully defined +// the naive implementation would enter the same apply$body in the same scope twice +trait F[T, U] { def apply(x: T): U } + +class C { + def app[T, U](x: T)(f: F[T, U]): U = f(x) + app(1)(x => List(x)) + app(2)(x => List(x)) +}
\ No newline at end of file diff --git a/test/files/pos/t6221.scala b/test/files/pos/t6221.scala index dd7776f596..34f02859f3 100644 --- a/test/files/pos/t6221.scala +++ b/test/files/pos/t6221.scala @@ -7,23 +7,27 @@ class MyCollection[A] { class OtherFunc[-A, +B] {} object Test { - implicit def functionToMyFunc[A, B](f: A => B): MyFunc[A, B] = new MyFunc + implicit def functionToMyFunc[A, B](f: A => B): MyFunc[A, B] = new MyFunc // = new MyFunc[A,Nothing](); - implicit def otherFuncToMyFunc[A, B](f: OtherFunc[A, B]): MyFunc[A, B] = new MyFunc + implicit def otherFuncToMyFunc[A, B](f: OtherFunc[A, B]): MyFunc[A, B] = new MyFunc // = new MyFunc[A,Nothing](); def main(args: Array[String]) { val col = new MyCollection[Int] // Doesn't compile: error: missing parameter type for expanded function ((x$1) => x$1.toString) println(col.map(_.toString)) + // scala.this.Predef.println(col.map[String](Test.this.functionToMyFunc[Int, String](((x$1: Int) => x$1.toString())))); // Doesn't compile: error: missing parameter type println(col.map(x => x.toString)) + // scala.this.Predef.println(col.map[String](Test.this.functionToMyFunc[Int, String](((x: Int) => x.toString())))); // Does compile println(col.map((x: Int) => x.toString)) + // scala.this.Predef.println(col.map[String](Test.this.functionToMyFunc[Int, String](((x: Int) => x.toString())))); // Does compile (even though type params of OtherFunc not given) println(col.map(new OtherFunc)) + // scala.this.Predef.println(col.map[Nothing](Test.this.otherFuncToMyFunc[Any, Nothing](new OtherFunc[Any,Nothing]()))) } }
\ No newline at end of file |