Skip to content

Commit 78e0871

Browse files
committed
Create placeholder symbols for function parameters when checking callee and improve type variable instantiation logic
1 parent 0bb0433 commit 78e0871

File tree

8 files changed

+119
-12
lines changed

8 files changed

+119
-12
lines changed

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

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
19161916
expr1.tpe
19171917
case _ =>
19181918
val outerCtx = ctx
1919-
val nestedCtx = outerCtx.fresh.setNewTyperState()
1919+
// Create placeholder symbols for function parameters to shadow outer bindings.
1920+
// This ensures that when typing `expr`, references in it won't incorrectly
1921+
// resolve to outer scope variables with the same names as the parameters.
1922+
val paramSyms = params.map(p =>
1923+
newSymbol(ctx.owner, p.name, Flags.Param, WildcardType, coord = p.span))
1924+
val nestedCtx = outerCtx.fresh.setNewTyperState().setScope(newScopeWith(paramSyms*))
19201925
inContext(nestedCtx) {
19211926
val protoArgs = args.map(_.withType(WildcardType))
19221927
val callProto = FunProto(protoArgs, WildcardType)(this, app.applyKind)
@@ -1933,19 +1938,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
19331938
NoType
19341939
}
19351940

1936-
/** Try to instantiate one type variable bounded by function types that appear
1941+
/** Find one instantiatable type variable bounded by function types that appear
19371942
* deeply inside `tp`, including union or intersection types.
19381943
*/
1939-
def tryToInstantiateDeeply(tp: Type): Boolean = tp.dealias match
1944+
def instantiatableTypeVar(tp: Type): Type = tp.dealias match
19401945
case tp: AndOrType =>
1941-
tryToInstantiateDeeply(tp.tp1)
1942-
|| tryToInstantiateDeeply(tp.tp2)
1946+
val t1 = instantiatableTypeVar(tp.tp1)
1947+
if t1.exists then t1
1948+
else instantiatableTypeVar(tp.tp2)
19431949
case tp: FlexibleType =>
1944-
tryToInstantiateDeeply(tp.hi)
1950+
instantiatableTypeVar(tp.hi)
19451951
case tp: TypeVar if isConstrainedByFunctionType(tp) =>
19461952
// Only instantiate if the type variable is constrained by function types
1947-
isFullyDefined(tp, ForceDegree.flipBottom)
1948-
case _ => false
1953+
tp
1954+
case _ => NoType
19491955

19501956
def isConstrainedByFunctionType(tvar: TypeVar): Boolean =
19511957
val origin = tvar.origin
@@ -1961,16 +1967,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
19611967
case _ => false
19621968
containsFunctionType(bounds.lo) || containsFunctionType(bounds.hi)
19631969

1964-
if untpd.isFunctionWithUnknownParamType(tree) && !calleeType.exists then
1970+
if untpd.isFunctionWithUnknownParamType(tree) then
19651971
// Try to instantiate `pt` when possible.
19661972
// * If `pt` is a type variable, we try to instantiate it directly.
19671973
// * If `pt` is a more complex type, we try to instantiate it deeply by searching
19681974
// a nested type variable bounded by a function type to help infer parameter types.
19691975
// If it does not work the error will be reported later in `inferredParam`,
19701976
// when we try to infer the parameter type.
1971-
pt match
1972-
case pt: TypeVar => isFullyDefined(pt, ForceDegree.flipBottom)
1973-
case _ => tryToInstantiateDeeply(pt)
1977+
// Note: we only check the `calleeType` if there is a TypeVar to instantiate to
1978+
// prioritize inferring from the callee.
1979+
val tp = if pt.isInstanceOf[TypeVar] then pt else instantiatableTypeVar(pt)
1980+
if tp.exists && !calleeType.exists then
1981+
isFullyDefined(tp, ForceDegree.flipBottom)
19741982

19751983
val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length, tree.srcPos)
19761984

tests/pos/i12679.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ object Example:
77

88
def example[F[_]](maybeQux: Option[String], bool: Boolean) =
99
maybeQux.fold(foo[F](bool))(foo[F](_))
10+
11+
object Example2:
12+
def foo(qux: String, quux: String = ""): Unit = ???
13+
14+
def foo(qux: Boolean): Unit = ???
15+
16+
def example(maybeQux: Option[String], bool: Boolean) =
17+
maybeQux.fold(foo(bool))(s => foo(s))

tests/pos/i24686.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
def Test = Seq.empty[DenseMatrix[Double]].reduce(DenseMatrix.horzcat(_, _))
2+
3+
trait Matrix[T]
4+
trait DenseMatrix[T] extends Matrix[T]
5+
6+
object DenseMatrix:
7+
def horzcat[M, V](matrices: M*)(using OpSet.InPlaceImpl2[DenseMatrix[V], M]): DenseMatrix[V] = ???
8+
9+
object OpSet extends HasOps:
10+
trait InPlaceImpl2[V1, V2]
11+
12+
trait HasOps
13+
object HasOps extends DenseMatrixExpandedOps with DensMatrixLowPriority
14+
15+
trait DenseMatrixExpandedOps:
16+
given OpSet.InPlaceImpl2[DenseMatrix[Double], DenseMatrix[Double]] = ???
17+
18+
trait DensMatrixLowPriority extends LowPriorityDenseMatrix1
19+
trait LowPriorityDenseMatrix1:
20+
given [V]: OpSet.InPlaceImpl2[DenseMatrix[V], Matrix[V]] = ???

tests/pos/i24689a.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import java.io.*
2+
import java.nio.file.*
3+
4+
abstract class Using[Source, A]:
5+
def apply[R](src: Source)(f: A => R): R = ???
6+
7+
object Using:
8+
val fileInputStream: Using[Path, InputStream] = ???
9+
10+
def transfer(in: Path, out: OutputStream): Unit =
11+
Using.fileInputStream(in)(in => transfer(in, out))
12+
13+
def transfer(in: InputStream, out: OutputStream): Unit = ???

tests/pos/i24689b.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
trait A
2+
trait B
3+
4+
object Test1:
5+
def foo(f: B => Unit) = ???
6+
def transfer(in: A): Unit =
7+
foo(in => transfer(in))
8+
foo(transfer)
9+
foo(transfer(_))
10+
def transfer(in: B): Unit = ???
11+
12+
object Test2:
13+
def foo[T <: (B => Unit)](f: T) = ???
14+
def transfer(in: A): Unit =
15+
foo(in => transfer(in))
16+
foo(transfer)
17+
foo(transfer(_))
18+
def transfer(in: B): Unit = ???

tests/pos/i24694/A_1.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
trait Show[T]
2+
object Show:
3+
given [A: Show]: Show[List[A]] = ???
4+
5+
trait Eq[A] extends Any, Serializable
6+
object Eq:
7+
def fromUniversalEquals[A]: Eq[A] = ???
8+
9+
object expect:
10+
def same[A](expected: A, found: A)(using
11+
eqA: Eq[A] = Eq.fromUniversalEquals[A],
12+
showA: Show[A]
13+
): Unit = ???
14+
15+
sealed trait XmlEvent
16+
object XmlEvent:
17+
given Show[XmlEvent] = ???

tests/pos/i24694/B_2.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def test(input: Option[List[XmlEvent]]) =
2+
val works =
3+
expect.same(List.empty[XmlEvent], input.get)
4+
val fails = input.map: tokens =>
5+
expect.same(List.empty[XmlEvent], tokens)

tests/pos/i24696.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
trait Schema[A]
2+
object Schema:
3+
final case class Either[A, B](left: Schema[A], right: Schema[B]) extends Schema[scala.util.Either[A, B]]
4+
trait DecodeError
5+
6+
object DynamicValue:
7+
final case class LeftValue(value: DynamicValue) extends DynamicValue
8+
final case class RightValue(value: DynamicValue) extends DynamicValue
9+
10+
sealed trait DynamicValue:
11+
self =>
12+
def toTypedValueLazyError[A](using schema: Schema[A]): Either[DecodeError, A] =
13+
(self, schema) match
14+
case (DynamicValue.LeftValue(value), Schema.Either(schema1, _)) =>
15+
value.toTypedValueLazyError(using schema1).map(Left(_))
16+
case (DynamicValue.RightValue(value), Schema.Either(_, schema1)) =>
17+
value.toTypedValueLazyError(using schema1).map(Right(_))
18+
case _ => ???

0 commit comments

Comments
 (0)