Skip to content

Commit c1d3abd

Browse files
committed
Don't retypecheck erroneous arguments when fixing function
Don't retypecheck erroneous arguments when trying conversions or extensions on the function part. Generally, we should avoid typechecking untyped trees several times, this can quickly explode exponentially. Fixes #12941
1 parent c3f614b commit c1d3abd

File tree

15 files changed

+101
-19
lines changed

15 files changed

+101
-19
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,8 +908,9 @@ trait Applications extends Compatibility {
908908
* part. Return an optional value to indicate success.
909909
*/
910910
def tryWithImplicitOnQualifier(fun1: Tree, proto: FunProto)(using Context): Option[Tree] =
911-
if (ctx.mode.is(Mode.SynthesizeExtMethodReceiver))
911+
if ctx.mode.is(Mode.SynthesizeExtMethodReceiver) || proto.hasErrorArg then
912912
// Suppress insertion of apply or implicit conversion on extension method receiver
913+
// or if argument is erroneous by itself.
913914
None
914915
else
915916
tryInsertImplicitOnQualifier(fun1, proto, ctx.typerState.ownedVars) flatMap { fun2 =>

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Contexts._, Types._, Denotations._, Names._, StdNames._, NameOps._, Symbo
88
import NameKinds.DepParamName
99
import Trees._
1010
import Constants._
11-
import util.{Stats, SimpleIdentityMap}
11+
import util.{Stats, SimpleIdentityMap, SimpleIdentitySet}
1212
import Decorators._
1313
import Uniques._
1414
import config.Printers.typr
@@ -282,6 +282,9 @@ object ProtoTypes {
282282
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
283283
var typedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.empty
284284

285+
/** The argument that produced errors during typing */
286+
var errorArgs: SimpleIdentitySet[untpd.Tree] = SimpleIdentitySet.empty
287+
285288
/** The tupled or untupled version of this prototype, if it has been computed */
286289
var tupledDual: Type = NoType
287290

@@ -341,6 +344,9 @@ object ProtoTypes {
341344
case _ => false
342345
}
343346

347+
/** Did an argument produce an error when typing? */
348+
def hasErrorArg = !state.errorArgs.isEmpty
349+
344350
private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean)(using Context): Tree = {
345351
var targ = state.typedArg(arg)
346352
if (targ == null)
@@ -357,8 +363,11 @@ object ProtoTypes {
357363
targ = arg.withType(WildcardType)
358364
case _ =>
359365
targ = typerFn(arg)
360-
if (!ctx.reporter.hasUnreportedErrors)
366+
if ctx.reporter.hasUnreportedErrors then
367+
state.errorArgs += arg
368+
else
361369
state.typedArg = state.typedArg.updated(arg, targ)
370+
state.errorArgs -= arg
362371
}
363372
targ
364373
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Error: tests/neg-custom-args/deprecation/t3235-minimal-v2.scala:3:16 ------------------------------------------------
2+
3 | assert(math.round(123456789) == 123456789) // error
3+
| ^^^^^^^^^^
4+
|method round in package scala.math is deprecated since 2.11.0: This is an integer type; there is no reason to round it. Perhaps you meant to call this with a floating-point value?
5+
-- Error: tests/neg-custom-args/deprecation/t3235-minimal-v2.scala:4:16 ------------------------------------------------
6+
4 | assert(math.round(1234567890123456789L) == 1234567890123456789L) // error
7+
| ^^^^^^^^^^
8+
|method round in package scala.math is deprecated since 2.11.0: This is an integer type; there is no reason to round it. Perhaps you meant to call this with a floating-point value?
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
assert(math.round(123456789) == 123456789) // error
4+
assert(math.round(1234567890123456789L) == 1234567890123456789L) // error
5+
}
6+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
assert(1234567890123456789L.round == 1234567890123456789L) // error
4+
}
5+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg-custom-args/deprecation/t3235-minimal.scala:3:21 ---------------------------------------------------
2+
3 | assert(123456789.round == 123456789) // error
3+
| ^^^^^^^^^^^^^^^
4+
|method round in class RichInt is deprecated since 2.11.0: this is an integer type; there is no reason to round it. Perhaps you meant to call this on a floating-point value?
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
object Test {
22
def main(args: Array[String]): Unit = {
33
assert(123456789.round == 123456789) // error
4-
assert(math.round(123456789) == 123456789) // error
5-
assert(1234567890123456789L.round == 1234567890123456789L) // error
6-
assert(math.round(1234567890123456789L) == 1234567890123456789L) // error
74
}
85
}

tests/neg/i12941.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
object A:
2+
def myFun(op: String ?=> Unit) = ()
3+
4+
@main def func: Unit =
5+
A.myFun {
6+
val res: String = summon[String]
7+
println(ress) // error
8+
}
9+
10+
class I:
11+
def runSth: Int = 1
12+
13+
abstract class A:
14+
def myFun(op: I ?=> Unit) =
15+
op(using I())
16+
1
17+
18+
class B extends A
19+
20+
def assertEquals(x: String, y: Int, z: Int): Unit = ()
21+
22+
@main def hello: Unit =
23+
24+
B().myFun {
25+
val res = summon[I].runSth
26+
assertEquals("", 1, res, "asd") // error
27+
println("Hello!")
28+
}

tests/neg/i8861.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object Test {
2020
)
2121
def minimalFail[M](c: Container { type A = M }): M = c.visit(
2222
int = vi => vi.i : vi.A,
23-
str = vs => vs.t : vs.A // error // error
23+
str = vs => vs.t : vs.A // error
2424
)
2525

2626
def main(args: Array[String]): Unit = {

tests/neg/i903.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i903.scala:3:16 ---------------------------------------------------------------
2+
3 | "".contains("", (_: Int)) // error
3+
| ^^^^^^^^^^^^
4+
| Found: (String, Int)
5+
| Required: CharSequence
6+
7+
longer explanation available when compiling with `-explain`

tests/neg/i903.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Test {
2+
def test2 =
3+
"".contains("", (_: Int)) // error
4+
}

tests/neg/zipped.check

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- [E007] Type Mismatch Error: tests/neg/zipped.scala:6:10 -------------------------------------------------------------
2+
6 | .map( (x: (Int, Int, Int)) => x match { case (x, y, z) => x + y + z }) // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| Found: ((Int, Int, Int)) => Int
5+
| Required: (Int, Int, Int) => Int
6+
7+
longer explanation available when compiling with `-explain`
8+
-- [E086] Syntax Error: tests/neg/zipped.scala:9:12 --------------------------------------------------------------------
9+
9 | .map( x => x match { case (x, y, z) => x + y + z }) // error
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11+
| Wrong number of parameters, expected: 3

tests/neg/zipped.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object Test {
2+
val xs: List[Int] = ???
3+
4+
// Does not work anynore since auto(un)tupling does not work after conversions
5+
xs.lazyZip(xs).lazyZip(xs)
6+
.map( (x: (Int, Int, Int)) => x match { case (x, y, z) => x + y + z }) // error
7+
8+
xs.lazyZip(xs).lazyZip(xs)
9+
.map( x => x match { case (x, y, z) => x + y + z }) // error
10+
}

tests/pos/i903.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,4 @@ object Test {
1515
// f.apply(0)
1616
// ^
1717
}
18-
19-
def test2 = {
20-
val f = "".contains("", (_: Int)) // dotc:
21-
f.apply(0)
22-
// sandbox/eta.scala:18: error: apply is not a member of Boolean(f)
23-
// f.apply(0)
24-
// ^
25-
}
2618
}

tests/pos/zipped.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ object Test {
2323
.map( (x: Int, y: Int, z: Int) => x + y + z ) // OK
2424

2525
// 4. The single parameter map works through an inserted conversion
26-
xs.lazyZip(xs).lazyZip(xs)
27-
.map( (x: (Int, Int, Int)) => x match { case (x, y, z) => x + y + z }) // now also OK
26+
//xs.lazyZip(xs).lazyZip(xs)
27+
// .map( (x: (Int, Int, Int)) => x match { case (x, y, z) => x + y + z }) // now also OK
2828

2929
// 5. If we leave out the parameter type, it now works as well.
30-
xs.lazyZip(xs).lazyZip(xs)
31-
.map( x => x match { case (x, y, z) => x + y + z }) // now also OK
30+
//xs.lazyZip(xs).lazyZip(xs)
31+
// .map( x => x match { case (x, y, z) => x + y + z }) // now also OK
3232

3333
// This means that the following works in Dotty 3.0 as well as 3.x
3434
for ((x, y, z) <- xs.lazyZip(xs).lazyZip(xs)) yield x + y + z

0 commit comments

Comments
 (0)