Skip to content

Commit 97677cc

Browse files
Do not compute protoFormal if param.tpt is empty (#18288)
This was accidentally moved before of the `if (!param.tpt.isEmpty)` guard in 0f7c3ab#diff-8c9ece1772bd78160fc1c31e988664586c9df566a1d22ff99ef99dd6d5627a90R1534. `protoFormal` has a side effect (error reporting) that caused this issue. Fixes #18276
2 parents 18df4ed + 495bcd2 commit 97677cc

File tree

3 files changed

+48
-25
lines changed

3 files changed

+48
-25
lines changed

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

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,32 +1606,31 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
16061606
if desugared.isEmpty then
16071607
val inferredParams: List[untpd.ValDef] =
16081608
for ((param, i) <- params.zipWithIndex) yield
1609-
val (formalBounds, isErased) = protoFormal(i)
1610-
val param0 =
1611-
if (!param.tpt.isEmpty) param
1612-
else
1613-
val formal = formalBounds.loBound
1614-
val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing
1615-
val knownFormal = isFullyDefined(formal, ForceDegree.failBottom)
1616-
// If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound,
1617-
// try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala)
1618-
val paramType =
1619-
// Strip inferred erased annotation, to avoid accidentally inferring erasedness
1620-
val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal
1621-
if knownFormal && !isBottomFromWildcard then
1622-
formal0
1623-
else
1624-
inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse(
1625-
if knownFormal then formal0
1626-
else errorType(AnonymousFunctionMissingParamType(param, tree, inferredType = formal, expectedType = pt), param.srcPos)
1627-
)
1628-
val paramTpt = untpd.TypedSplice(
1629-
(if knownFormal then InferredTypeTree() else untpd.TypeTree())
1630-
.withType(paramType.translateFromRepeated(toArray = false))
1631-
.withSpan(param.span.endPos)
1609+
if (!param.tpt.isEmpty) param
1610+
else
1611+
val (formalBounds, isErased) = protoFormal(i)
1612+
val formal = formalBounds.loBound
1613+
val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing
1614+
val knownFormal = isFullyDefined(formal, ForceDegree.failBottom)
1615+
// If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound,
1616+
// try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala)
1617+
val paramType =
1618+
// Strip inferred erased annotation, to avoid accidentally inferring erasedness
1619+
val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal
1620+
if knownFormal && !isBottomFromWildcard then
1621+
formal0
1622+
else
1623+
inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse(
1624+
if knownFormal then formal0
1625+
else errorType(AnonymousFunctionMissingParamType(param, tree, inferredType = formal, expectedType = pt), param.srcPos)
16321626
)
1633-
cpy.ValDef(param)(tpt = paramTpt)
1634-
if isErased then param0.withAddedFlags(Flags.Erased) else param0
1627+
val paramTpt = untpd.TypedSplice(
1628+
(if knownFormal then InferredTypeTree() else untpd.TypeTree())
1629+
.withType(paramType.translateFromRepeated(toArray = false))
1630+
.withSpan(param.span.endPos)
1631+
)
1632+
val param0 = cpy.ValDef(param)(tpt = paramTpt)
1633+
if isErased then param0.withAddedFlags(Flags.Erased) else param0
16351634
desugared = desugar.makeClosure(Nil, inferredParams, fnBody, resultTpt, tree.span)
16361635

16371636
typed(desugared, pt)

tests/pos/i18276a.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import scala.language.implicitConversions
2+
3+
case class Assign(left: String, right: String)
4+
class SyntaxAnalyser extends ParsersBase {
5+
val x: Parser[String ~ String] = ???
6+
val y: Parser[Assign] = x.map(Assign.apply)
7+
}
8+
9+
class ParsersBase {
10+
trait ~[+T, +U]
11+
abstract class Parser[+T]:
12+
def map[U](f: T => U): Parser[U] = ???
13+
14+
given [A, B, X]: Conversion[(A, B) => X, (A ~ B) => X] = ???
15+
}

tests/pos/i18276b.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import scala.language.implicitConversions
2+
3+
def foo(a: Int): Int = ???
4+
def bar(f: () => Int): Int = ???
5+
6+
given f: Conversion[Int => Int, () => Int] = ???
7+
8+
def test1: Int = bar(foo) // implicit conversion applied to foo
9+
def test2: Int = bar(f(foo))

0 commit comments

Comments
 (0)