Skip to content

Commit

Permalink
Do not compute protoFormal if param.tpt is empty (#18288)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nicolasstucki committed Jul 26, 2023
2 parents 18df4ed + 495bcd2 commit 97677cc
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 25 deletions.
49 changes: 24 additions & 25 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1606,32 +1606,31 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
if desugared.isEmpty then
val inferredParams: List[untpd.ValDef] =
for ((param, i) <- params.zipWithIndex) yield
val (formalBounds, isErased) = protoFormal(i)
val param0 =
if (!param.tpt.isEmpty) param
else
val formal = formalBounds.loBound
val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing
val knownFormal = isFullyDefined(formal, ForceDegree.failBottom)
// If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound,
// try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala)
val paramType =
// Strip inferred erased annotation, to avoid accidentally inferring erasedness
val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal
if knownFormal && !isBottomFromWildcard then
formal0
else
inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse(
if knownFormal then formal0
else errorType(AnonymousFunctionMissingParamType(param, tree, inferredType = formal, expectedType = pt), param.srcPos)
)
val paramTpt = untpd.TypedSplice(
(if knownFormal then InferredTypeTree() else untpd.TypeTree())
.withType(paramType.translateFromRepeated(toArray = false))
.withSpan(param.span.endPos)
if (!param.tpt.isEmpty) param
else
val (formalBounds, isErased) = protoFormal(i)
val formal = formalBounds.loBound
val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing
val knownFormal = isFullyDefined(formal, ForceDegree.failBottom)
// If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound,
// try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala)
val paramType =
// Strip inferred erased annotation, to avoid accidentally inferring erasedness
val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal
if knownFormal && !isBottomFromWildcard then
formal0
else
inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse(
if knownFormal then formal0
else errorType(AnonymousFunctionMissingParamType(param, tree, inferredType = formal, expectedType = pt), param.srcPos)
)
cpy.ValDef(param)(tpt = paramTpt)
if isErased then param0.withAddedFlags(Flags.Erased) else param0
val paramTpt = untpd.TypedSplice(
(if knownFormal then InferredTypeTree() else untpd.TypeTree())
.withType(paramType.translateFromRepeated(toArray = false))
.withSpan(param.span.endPos)
)
val param0 = cpy.ValDef(param)(tpt = paramTpt)
if isErased then param0.withAddedFlags(Flags.Erased) else param0
desugared = desugar.makeClosure(Nil, inferredParams, fnBody, resultTpt, tree.span)

typed(desugared, pt)
Expand Down
15 changes: 15 additions & 0 deletions tests/pos/i18276a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.language.implicitConversions

case class Assign(left: String, right: String)
class SyntaxAnalyser extends ParsersBase {
val x: Parser[String ~ String] = ???
val y: Parser[Assign] = x.map(Assign.apply)
}

class ParsersBase {
trait ~[+T, +U]
abstract class Parser[+T]:
def map[U](f: T => U): Parser[U] = ???

given [A, B, X]: Conversion[(A, B) => X, (A ~ B) => X] = ???
}
9 changes: 9 additions & 0 deletions tests/pos/i18276b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import scala.language.implicitConversions

def foo(a: Int): Int = ???
def bar(f: () => Int): Int = ???

given f: Conversion[Int => Int, () => Int] = ???

def test1: Int = bar(foo) // implicit conversion applied to foo
def test2: Int = bar(f(foo))

0 comments on commit 97677cc

Please sign in to comment.