Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1916,7 +1916,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
expr1.tpe
case _ =>
val outerCtx = ctx
val nestedCtx = outerCtx.fresh.setNewTyperState()
// Create placeholder symbols for function parameters to shadow outer bindings.
// This ensures that when typing `expr`, references in it won't incorrectly
// resolve to outer scope variables with the same names as the parameters.
val paramSyms = params.map(p =>
newSymbol(ctx.owner, p.name, Flags.Param, WildcardType, coord = p.span))
val nestedCtx = outerCtx.fresh.setNewTyperState().setScope(newScopeWith(paramSyms*))
inContext(nestedCtx) {
val protoArgs = args.map(_.withType(WildcardType))
val callProto = FunProto(protoArgs, WildcardType)(this, app.applyKind)
Expand All @@ -1933,19 +1938,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
NoType
}

/** Try to instantiate one type variable bounded by function types that appear
/** Find one instantiatable type variable bounded by function types that appear
* deeply inside `tp`, including union or intersection types.
*/
def tryToInstantiateDeeply(tp: Type): Boolean = tp.dealias match
def instantiatableTypeVar(tp: Type): Type = tp.dealias match
case tp: AndOrType =>
tryToInstantiateDeeply(tp.tp1)
|| tryToInstantiateDeeply(tp.tp2)
val t1 = instantiatableTypeVar(tp.tp1)
if t1.exists then t1
else instantiatableTypeVar(tp.tp2)
case tp: FlexibleType =>
tryToInstantiateDeeply(tp.hi)
instantiatableTypeVar(tp.hi)
case tp: TypeVar if isConstrainedByFunctionType(tp) =>
// Only instantiate if the type variable is constrained by function types
isFullyDefined(tp, ForceDegree.flipBottom)
case _ => false
tp
case _ => NoType

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

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

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

Expand Down
8 changes: 8 additions & 0 deletions tests/pos/i12679.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ object Example:

def example[F[_]](maybeQux: Option[String], bool: Boolean) =
maybeQux.fold(foo[F](bool))(foo[F](_))

object Example2:
def foo(qux: String, quux: String = ""): Unit = ???

def foo(qux: Boolean): Unit = ???

def example(maybeQux: Option[String], bool: Boolean) =
maybeQux.fold(foo(bool))(s => foo(s))
20 changes: 20 additions & 0 deletions tests/pos/i24686.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def Test = Seq.empty[DenseMatrix[Double]].reduce(DenseMatrix.horzcat(_, _))

trait Matrix[T]
trait DenseMatrix[T] extends Matrix[T]

object DenseMatrix:
def horzcat[M, V](matrices: M*)(using OpSet.InPlaceImpl2[DenseMatrix[V], M]): DenseMatrix[V] = ???

object OpSet extends HasOps:
trait InPlaceImpl2[V1, V2]

trait HasOps
object HasOps extends DenseMatrixExpandedOps with DensMatrixLowPriority

trait DenseMatrixExpandedOps:
given OpSet.InPlaceImpl2[DenseMatrix[Double], DenseMatrix[Double]] = ???

trait DensMatrixLowPriority extends LowPriorityDenseMatrix1
trait LowPriorityDenseMatrix1:
given [V]: OpSet.InPlaceImpl2[DenseMatrix[V], Matrix[V]] = ???
13 changes: 13 additions & 0 deletions tests/pos/i24689a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import java.io.*
import java.nio.file.*

abstract class Using[Source, A]:
def apply[R](src: Source)(f: A => R): R = ???

object Using:
val fileInputStream: Using[Path, InputStream] = ???

def transfer(in: Path, out: OutputStream): Unit =
Using.fileInputStream(in)(in => transfer(in, out))

def transfer(in: InputStream, out: OutputStream): Unit = ???
18 changes: 18 additions & 0 deletions tests/pos/i24689b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
trait A
trait B

object Test1:
def foo(f: B => Unit) = ???
def transfer(in: A): Unit =
foo(in => transfer(in))
foo(transfer)
foo(transfer(_))
def transfer(in: B): Unit = ???

object Test2:
def foo[T <: (B => Unit)](f: T) = ???
def transfer(in: A): Unit =
foo(in => transfer(in))
foo(transfer)
foo(transfer(_))
def transfer(in: B): Unit = ???
17 changes: 17 additions & 0 deletions tests/pos/i24694/A_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
trait Show[T]
object Show:
given [A: Show]: Show[List[A]] = ???

trait Eq[A] extends Any, Serializable
object Eq:
def fromUniversalEquals[A]: Eq[A] = ???

object expect:
def same[A](expected: A, found: A)(using
eqA: Eq[A] = Eq.fromUniversalEquals[A],
showA: Show[A]
): Unit = ???

sealed trait XmlEvent
object XmlEvent:
given Show[XmlEvent] = ???
5 changes: 5 additions & 0 deletions tests/pos/i24694/B_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
def test(input: Option[List[XmlEvent]]) =
val works =
expect.same(List.empty[XmlEvent], input.get)
val fails = input.map: tokens =>
expect.same(List.empty[XmlEvent], tokens)
18 changes: 18 additions & 0 deletions tests/pos/i24696.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
trait Schema[A]
object Schema:
final case class Either[A, B](left: Schema[A], right: Schema[B]) extends Schema[scala.util.Either[A, B]]
trait DecodeError

object DynamicValue:
final case class LeftValue(value: DynamicValue) extends DynamicValue
final case class RightValue(value: DynamicValue) extends DynamicValue

sealed trait DynamicValue:
self =>
def toTypedValueLazyError[A](using schema: Schema[A]): Either[DecodeError, A] =
(self, schema) match
case (DynamicValue.LeftValue(value), Schema.Either(schema1, _)) =>
value.toTypedValueLazyError(using schema1).map(Left(_))
case (DynamicValue.RightValue(value), Schema.Either(_, schema1)) =>
value.toTypedValueLazyError(using schema1).map(Right(_))
case _ => ???
Loading