From f0e54c59520566b8d9d2b4dae8a4802de6b3a843 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Oct 2015 09:53:58 +0100 Subject: [PATCH 1/4] Allow pattern matching anonymous functions of arity > 1 This is sepcified in Sec. 8.5 of the SLS. Fixes #873. Review by @smarter. --- src/dotty/tools/dotc/ast/Desugar.scala | 16 +++++++++++----- src/dotty/tools/dotc/typer/Typer.scala | 6 +++++- tests/pos/i873.scala | 4 ++++ 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 tests/pos/i873.scala diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index d1f101283e05..fe6e48aa87a6 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -556,14 +556,20 @@ object desugar { DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(synthetic), Closure(Nil, Ident(nme.ANON_FUN), EmptyTree)) - /** Expand partial function + /** If `nparams` == 1, expand partial function + * * { cases } * ==> - * x$0 => x$0 match { cases } + * x$1 => x$1 match { cases } + * + * If `nparams` != 1, expand instead to + * + * (x$0, ..., x${n-1}) => (x$0, ..., x${n-1}) match { cases } */ - def makeCaseLambda(cases: List[CaseDef])(implicit ctx: Context) = { - val param = makeSyntheticParameter() - Function(param :: Nil, Match(Ident(param.name), cases)) + def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1)(implicit ctx: Context) = { + val params = (1 to nparams).toList.map(makeSyntheticParameter(_)) + val selector = makeTuple(params.map(p => Ident(p.name))) + Function(params, Match(selector, cases)) } /** Add annotation with class `cls` to tree: diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 52ea32bbc1be..4c07e07dee8b 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -629,7 +629,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { tree.selector match { case EmptyTree => - typed(desugar.makeCaseLambda(tree.cases) withPos tree.pos, pt) + val arity = pt match { + case defn.FunctionType(args, _) => args.length + case _ => 1 + } + typed(desugar.makeCaseLambda(tree.cases, arity) withPos tree.pos, pt) case _ => val sel1 = typedExpr(tree.selector) val selType = widenForMatchSelector( diff --git a/tests/pos/i873.scala b/tests/pos/i873.scala new file mode 100644 index 000000000000..71c8a3959caf --- /dev/null +++ b/tests/pos/i873.scala @@ -0,0 +1,4 @@ +object Test { + def call(k: (Int, Int) => Unit): Unit = ??? + def test = call({ case (x, y) => ()}) +} From 2a4d7bc68dde060458cb3692cc272968f096c7d4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Oct 2015 11:00:24 +0100 Subject: [PATCH 2/4] Fix case expansion for nullary functions. Always expand to at least one parameter. --- src/dotty/tools/dotc/ast/Desugar.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index fe6e48aa87a6..51b7e3a1fc49 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -562,12 +562,12 @@ object desugar { * ==> * x$1 => x$1 match { cases } * - * If `nparams` != 1, expand instead to + * If `nparams` > 1, expand instead to * - * (x$0, ..., x${n-1}) => (x$0, ..., x${n-1}) match { cases } + * (x$1, ..., x$n) => (x$0, ..., x${n-1}) match { cases } */ def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1)(implicit ctx: Context) = { - val params = (1 to nparams).toList.map(makeSyntheticParameter(_)) + val params = (1 to (nparams min 1)).toList.map(makeSyntheticParameter(_)) val selector = makeTuple(params.map(p => Ident(p.name))) Function(params, Match(selector, cases)) } From 9fbb9c9c9b6050183dd71b8541a63fc6ebf9e2a6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Oct 2015 13:59:11 +0100 Subject: [PATCH 3/4] Revert nullary function patch Nullary functions are handled by scalac, and dotty should do the same. --- src/dotty/tools/dotc/ast/Desugar.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 51b7e3a1fc49..1ef883bf2826 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -562,12 +562,12 @@ object desugar { * ==> * x$1 => x$1 match { cases } * - * If `nparams` > 1, expand instead to + * If `nparams` != 1, expand instead to * * (x$1, ..., x$n) => (x$0, ..., x${n-1}) match { cases } */ def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1)(implicit ctx: Context) = { - val params = (1 to (nparams min 1)).toList.map(makeSyntheticParameter(_)) + val params = (1 to nparams).toList.map(makeSyntheticParameter(_)) val selector = makeTuple(params.map(p => Ident(p.name))) Function(params, Match(selector, cases)) } From b80b179d6fbb92c8f6ff3616cec1f3aab5106799 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Oct 2015 19:11:19 +0100 Subject: [PATCH 4/4] Also handle SAM functions when adaptiing arity of case lambdas. --- src/dotty/tools/dotc/typer/Typer.scala | 27 +++++++++++++------------- tests/pos/i873.scala | 6 ++++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 4c07e07dee8b..15ce59c8b31a 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -502,6 +502,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2) } + private def decomposeProtoFunction(pt: Type, defaultArity: Int)(implicit ctx: Context): (List[Type], Type) = pt match { + case _ if defn.isFunctionType(pt) => + (pt.dealias.argInfos.init, pt.dealias.argInfos.last) + case SAMType(meth) => + val mt @ MethodType(_, paramTypes) = meth.info + (paramTypes, mt.resultType) + case _ => + (List.range(0, defaultArity) map alwaysWildcardType, WildcardType) + } + def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) @@ -509,15 +519,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt) else { val params = args.asInstanceOf[List[untpd.ValDef]] - val (protoFormals, protoResult): (List[Type], Type) = pt match { - case _ if defn.isFunctionType(pt) => - (pt.dealias.argInfos.init, pt.dealias.argInfos.last) - case SAMType(meth) => - val mt @ MethodType(_, paramTypes) = meth.info - (paramTypes, mt.resultType) - case _ => - (params map alwaysWildcardType, WildcardType) - } + val (protoFormals, protoResult) = decomposeProtoFunction(pt, params.length) def refersTo(arg: untpd.Tree, param: untpd.ValDef): Boolean = arg match { case Ident(name) => name == param.name @@ -629,11 +631,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { tree.selector match { case EmptyTree => - val arity = pt match { - case defn.FunctionType(args, _) => args.length - case _ => 1 - } - typed(desugar.makeCaseLambda(tree.cases, arity) withPos tree.pos, pt) + val (protoFormals, _) = decomposeProtoFunction(pt, 1) + typed(desugar.makeCaseLambda(tree.cases, protoFormals.length) withPos tree.pos, pt) case _ => val sel1 = typedExpr(tree.selector) val selType = widenForMatchSelector( diff --git a/tests/pos/i873.scala b/tests/pos/i873.scala index 71c8a3959caf..94f8d2c67599 100644 --- a/tests/pos/i873.scala +++ b/tests/pos/i873.scala @@ -1,4 +1,10 @@ object Test { def call(k: (Int, Int) => Unit): Unit = ??? def test = call({ case (x, y) => ()}) + + trait X extends Function1[Int, String] + implicit def f2x(f: Function1[Int, String]): X = ??? + ({case _ if "".isEmpty => ""} : X) // allowed, implicit view used to adapt + + // ({case _ if "".isEmpty => 0} : X) // expected String, found Int }