From c973d9bfe248b67e5ed6966c3d46848635430907 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Mon, 22 Jul 2024 20:02:12 +0200 Subject: [PATCH] Fail when a poly function value has a different number of type params than the expected poly function --- .../src/dotty/tools/dotc/typer/Typer.scala | 52 ++++++++++--------- tests/neg/i20533.check | 5 ++ tests/neg/i20533.scala | 6 +++ 3 files changed, 38 insertions(+), 25 deletions(-) create mode 100644 tests/neg/i20533.check create mode 100644 tests/neg/i20533.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5113a6380a78..db3bab6f766a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1912,32 +1912,34 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val untpd.Function(vparams: List[untpd.ValDef] @unchecked, body) = fun: @unchecked val dpt = pt.dealias - // If the expected type is a polymorphic function with the same number of - // type and value parameters, then infer the types of value parameters from the expected type. - val inferredVParams = dpt match - case defn.PolyFunctionOf(poly @ PolyType(_, mt: MethodType)) - if tparams.lengthCompare(poly.paramNames) == 0 && vparams.lengthCompare(mt.paramNames) == 0 => - vparams.zipWithConserve(mt.paramInfos): (vparam, formal) => - // Unlike in typedFunctionValue, `formal` cannot be a TypeBounds since - // it must be a valid method parameter type. - if vparam.tpt.isEmpty && isFullyDefined(formal, ForceDegree.failBottom) then - cpy.ValDef(vparam)(tpt = new untpd.InLambdaTypeTree(isResult = false, (tsyms, vsyms) => - // We don't need to substitute `mt` by `vsyms` because we currently disallow - // dependencies between value parameters of a closure. - formal.substParams(poly, tsyms.map(_.typeRef))) - ) - else vparam - case _ => - vparams - - val resultTpt = dpt match + dpt match case defn.PolyFunctionOf(poly @ PolyType(_, mt: MethodType)) => - untpd.InLambdaTypeTree(isResult = true, (tsyms, vsyms) => - mt.resultType.substParams(mt, vsyms.map(_.termRef)).substParams(poly, tsyms.map(_.typeRef))) - case _ => untpd.TypeTree() - - val desugared = desugar.makeClosure(tparams, inferredVParams, body, resultTpt, tree.span) - typed(desugared, pt) + if tparams.lengthCompare(poly.paramNames) == 0 && vparams.lengthCompare(mt.paramNames) == 0 then + // If the expected type is a polymorphic function with the same number of + // type and value parameters, then infer the types of value parameters from the expected type. + val inferredVParams = vparams.zipWithConserve(mt.paramInfos): (vparam, formal) => + // Unlike in typedFunctionValue, `formal` cannot be a TypeBounds since + // it must be a valid method parameter type. + if vparam.tpt.isEmpty && isFullyDefined(formal, ForceDegree.failBottom) then + cpy.ValDef(vparam)(tpt = new untpd.InLambdaTypeTree(isResult = false, (tsyms, vsyms) => + // We don't need to substitute `mt` by `vsyms` because we currently disallow + // dependencies between value parameters of a closure. + formal.substParams(poly, tsyms.map(_.typeRef))) + ) + else vparam + val resultTpt = + untpd.InLambdaTypeTree(isResult = true, (tsyms, vsyms) => + mt.resultType.substParams(mt, vsyms.map(_.termRef)).substParams(poly, tsyms.map(_.typeRef))) + val desugared = desugar.makeClosure(tparams, inferredVParams, body, resultTpt, tree.span) + typed(desugared, pt) + else + val msg = + em"""|Provided polymorphic function value doesn't match the expected type $dpt. + |Expected type should be a polymorphic function with the same number of type and value parameters.""" + errorTree(EmptyTree, msg, tree.srcPos) + case _ => + val desugared = desugar.makeClosure(tparams, vparams, body, untpd.TypeTree(), tree.span) + typed(desugared, pt) end typedPolyFunctionValue def typedClosure(tree: untpd.Closure, pt: Type)(using Context): Tree = { diff --git a/tests/neg/i20533.check b/tests/neg/i20533.check new file mode 100644 index 000000000000..45dfbd7f4b92 --- /dev/null +++ b/tests/neg/i20533.check @@ -0,0 +1,5 @@ +-- Error: tests/neg/i20533.scala:5:8 ----------------------------------------------------------------------------------- +5 | [X] => (x, y) => Map(x -> y) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Provided polymorphic function value doesn't match the expected type [X, Y] => (x$1: X, x$2: Y) => Map[X, Y]. + | Expected type should be a polymorphic function with the same number of type and value parameters. diff --git a/tests/neg/i20533.scala b/tests/neg/i20533.scala new file mode 100644 index 000000000000..20059bd795c6 --- /dev/null +++ b/tests/neg/i20533.scala @@ -0,0 +1,6 @@ +def mapF(h: [X, Y] => (X, Y) => Map[X, Y]): Unit = ??? + +def test = + mapF( + [X] => (x, y) => Map(x -> y) // error + )