From a1a89b62f4703d90c6ed117ed3de7ed1e5d0cf1c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 5 Jan 2020 23:38:28 +0100 Subject: [PATCH 1/5] Fix #7893: Less eager forcing of expected types for functions Don't force expected parameter types if the callee type of a closure is known. In this case, we have info info to fill in parameter types from the callee. --- .../src/dotty/tools/dotc/typer/Typer.scala | 63 ++++++++++--------- tests/pos/i7893.scala | 7 +++ 2 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 tests/pos/i7893.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 248706dc4db0..bd2a1af9a5a4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -955,35 +955,19 @@ class Typer extends Namer case _ => false } - pt match { - case pt: TypeVar if untpd.isFunctionWithUnknownParamType(tree) => - // try to instantiate `pt` if this is possible. If it does not - // work the error will be reported later in `inferredParam`, - // when we try to infer the parameter type. - isFullyDefined(pt, ForceDegree.noBottom) - case _ => - } - - val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length) + /** The function body to be returned in the closure. Can become a TypedSplice + * of a typed expression if this is necessary to infer a parameter type. + */ + var fnBody = tree.body def refersTo(arg: untpd.Tree, param: untpd.ValDef): Boolean = arg match { case Ident(name) => name == param.name case _ => false } - /** The function body to be returned in the closure. Can become a TypedSplice - * of a typed expression if this is necessary to infer a parameter type. - */ - var fnBody = tree.body - - /** A map from parameter names to unique positions where the parameter - * appears in the argument list of an application. - */ - var paramIndex = Map[Name, Int]() - /** If parameter `param` appears exactly once as an argument in `args`, - * the singleton list consisting of its position in `args`, otherwise `Nil`. - */ + * the singleton list consisting of its position in `args`, otherwise `Nil`. + */ def paramIndices(param: untpd.ValDef, args: List[untpd.Tree]): List[Int] = { def loop(args: List[untpd.Tree], start: Int): List[Int] = args match { case arg :: args1 => @@ -995,15 +979,20 @@ class Typer extends Namer if (allIndices.length == 1) allIndices else Nil } - /** If function is of the form - * (x1, ..., xN) => f(... x1, ..., XN, ...) - * where each `xi` occurs exactly once in the argument list of `f` (in - * any order), the type of `f`, otherwise NoType. - * Updates `fnBody` and `paramIndex` as a side effect. - * @post: If result exists, `paramIndex` is defined for the name of - * every parameter in `params`. - */ - def calleeType: Type = fnBody match { + /** A map from parameter names to unique positions where the parameter + * appears in the argument list of an application. + */ + var paramIndex = Map[Name, Int]() + + /** If function is of the form + * (x1, ..., xN) => f(... x1, ..., XN, ...) + * where each `xi` occurs exactly once in the argument list of `f` (in + * any order), the type of `f`, otherwise NoType. + * Updates `fnBody` and `paramIndex` as a side effect. + * @post: If result exists, `paramIndex` is defined for the name of + * every parameter in `params`. + */ + lazy val calleeType: Type = fnBody match { case app @ Apply(expr, args) => paramIndex = { for (param <- params; idx <- paramIndices(param, args)) @@ -1025,6 +1014,18 @@ class Typer extends Namer NoType } + pt match { + case pt: TypeVar + if untpd.isFunctionWithUnknownParamType(tree) && !calleeType.exists => + // try to instantiate `pt` if this is possible. If it does not + // work the error will be reported later in `inferredParam`, + // when we try to infer the parameter type. + isFullyDefined(pt, ForceDegree.noBottom) + case _ => + } + + val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length) + /** Two attempts: First, if expected type is fully defined pick this one. * Second, if function is of the form * (x1, ..., xN) => f(... x1, ..., XN, ...) diff --git a/tests/pos/i7893.scala b/tests/pos/i7893.scala new file mode 100644 index 000000000000..8d8626544133 --- /dev/null +++ b/tests/pos/i7893.scala @@ -0,0 +1,7 @@ +object Test { + val l1 = List(Predef.identity[Int](_)) + val lc1: List[Int => Int] = l1 + + val l2 = List(Predef.identity[Int](_)) + val lc2: List[Int => Int] = l2 +} \ No newline at end of file From f7867bd391d6565346013d4193317cf6fc2aecb5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 6 Jan 2020 07:43:56 +0100 Subject: [PATCH 2/5] Fix test case --- tests/pos/i7893.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pos/i7893.scala b/tests/pos/i7893.scala index 8d8626544133..f8704f53c25e 100644 --- a/tests/pos/i7893.scala +++ b/tests/pos/i7893.scala @@ -2,6 +2,6 @@ object Test { val l1 = List(Predef.identity[Int](_)) val lc1: List[Int => Int] = l1 - val l2 = List(Predef.identity[Int](_)) + val l2 = List(Predef.identity[Int]) val lc2: List[Int => Int] = l2 } \ No newline at end of file From 79fb94c5735a499af68ebb3db834611cc229a496 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 6 Jan 2020 08:06:47 +0100 Subject: [PATCH 3/5] Test case --- tests/pos/i7879.scala | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/pos/i7879.scala diff --git a/tests/pos/i7879.scala b/tests/pos/i7879.scala new file mode 100644 index 000000000000..c0eba43fbb8d --- /dev/null +++ b/tests/pos/i7879.scala @@ -0,0 +1,4 @@ +object Test { + val head1 +: _ = List(1).view + val _: Int = head1 +} \ No newline at end of file From f51b96a9ffe4b14477a520d59eb6cd81f89800b4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 6 Jan 2020 14:56:34 +0100 Subject: [PATCH 4/5] Fix #7879: Add failing test case --- tests/neg/i7879.scala | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/neg/i7879.scala diff --git a/tests/neg/i7879.scala b/tests/neg/i7879.scala new file mode 100644 index 000000000000..a5091886fe5e --- /dev/null +++ b/tests/neg/i7879.scala @@ -0,0 +1,4 @@ +object Test { + val head1 +: _ = List(1).view + val _: Int = head1 // error +} \ No newline at end of file From 0daea628528b7d59964475fce091445ca121fbb7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 6 Jan 2020 14:57:08 +0100 Subject: [PATCH 5/5] Revert "Test case" This reverts commit 79fb94c5735a499af68ebb3db834611cc229a496. --- tests/pos/i7879.scala | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 tests/pos/i7879.scala diff --git a/tests/pos/i7879.scala b/tests/pos/i7879.scala deleted file mode 100644 index c0eba43fbb8d..000000000000 --- a/tests/pos/i7879.scala +++ /dev/null @@ -1,4 +0,0 @@ -object Test { - val head1 +: _ = List(1).view - val _: Int = head1 -} \ No newline at end of file