Skip to content

Commit

Permalink
Instantiate argument type vars before implicit search (scala#19096)
Browse files Browse the repository at this point in the history
  • Loading branch information
EugeneFlesselle authored Jan 29, 2024
1 parent 1716bcd commit cce2933
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 10 deletions.
23 changes: 14 additions & 9 deletions compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -383,17 +383,20 @@ object Inferencing {
def isSkolemFree(tp: Type)(using Context): Boolean =
!tp.existsPart(_.isInstanceOf[SkolemType])

/** The list of uninstantiated type variables bound by some prefix of type `T` which
* occur in at least one formal parameter type of a prefix application.
/** The list of uninstantiated type variables bound by some prefix of type `T` or
* by arguments of an application prefix, which occur at least once as a formal type parameter
* of an application either from a prefix or an argument of an application node.
* Considered prefixes are:
* - The function `f` of an application node `f(e1, .., en)`
* - The function `f` of a type application node `f[T1, ..., Tn]`
* - The prefix `p` of a selection `p.f`.
* - The result expression `e` of a block `{s1; .. sn; e}`.
*/
def tvarsInParams(tree: Tree, locked: TypeVars)(using Context): List[TypeVar] = {
@tailrec def boundVars(tree: Tree, acc: List[TypeVar]): List[TypeVar] = tree match {
case Apply(fn, _) => boundVars(fn, acc)
def boundVars(tree: Tree, acc: List[TypeVar]): List[TypeVar] = tree match {
case Apply(fn, args) =>
val argTpVars = args.flatMap(boundVars(_, Nil))
boundVars(fn, acc ++ argTpVars)
case TypeApply(fn, targs) =>
val tvars = targs.filter(_.isInstanceOf[InferredTypeTree]).tpes.collect {
case tvar: TypeVar
Expand All @@ -406,16 +409,18 @@ object Inferencing {
case Block(_, expr) => boundVars(expr, acc)
case _ => acc
}
@tailrec def occurring(tree: Tree, toTest: List[TypeVar], acc: List[TypeVar]): List[TypeVar] =
def occurring(tree: Tree, toTest: List[TypeVar], acc: List[TypeVar]): List[TypeVar] =
if (toTest.isEmpty) acc
else tree match {
case Apply(fn, _) =>
case Apply(fn, args) =>
val argsOcc = args.flatMap(occurring(_, toTest, Nil))
val argsNocc = toTest.filterNot(argsOcc.contains)
fn.tpe.widen match {
case mtp: MethodType =>
val (occ, nocc) = toTest.partition(tvar => mtp.paramInfos.exists(tvar.occursIn))
occurring(fn, nocc, occ ::: acc)
val (occ, nocc) = argsNocc.partition(tvar => mtp.paramInfos.exists(tvar.occursIn))
occurring(fn, nocc, occ ::: argsOcc ::: acc)
case _ =>
occurring(fn, toTest, acc)
occurring(fn, argsNocc, argsOcc ::: acc)
}
case TypeApply(fn, targs) => occurring(fn, toTest, acc)
case Select(pre, _) => occurring(pre, toTest, acc)
Expand Down
17 changes: 17 additions & 0 deletions tests/pos/i18578.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

trait Animal
class Dog extends Animal

trait Ev[T]

given Ev[Dog] = ???
given Ev[Animal] = ???
given[T: Ev]: Ev[Set[T]] = ???

def f[T: Ev](v: T): Unit = ???

def main =
val s = Set(new Dog)
f(s) // Ok
f(Set(new Dog)) // Error before changes: Ambiguous given instances: both given instance given_Ev_Dog and given instance given_Ev_Animal match type Ev[T]

38 changes: 38 additions & 0 deletions tests/pos/i7586.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

trait Nat
case object Z extends Nat
case class S[N <: Nat](pred: N) extends Nat

type Z = Z.type
given zero: Z = Z
given succ[N <: Nat](using n: N): S[N] = S(n)

case class Sum[N <: Nat, M <: Nat, R <: Nat](result: R)

given sumZ[N <: Nat](using n: N): Sum[Z, N, N] = Sum(n)
given sumS[N <: Nat, M <: Nat, R <: Nat](
using sum: Sum[N, M, R]
): Sum[S[N], M, S[R]] = Sum(S(sum.result))

def add[N <: Nat, M <: Nat, R <: Nat](n: N, m: M)(
using sum: Sum[N, M, R]
): R = sum.result

case class Prod[N <: Nat, M <: Nat, R <: Nat](result: R)


@main def Test: Unit =

val n1: S[Z] = add(Z, S(Z))
summon[n1.type <:< S[Z]] // OK

val n3: S[S[S[Z]]] = add(S(S(Z)), S(Z))
summon[n3.type <:< S[S[S[Z]]]] // Ok

val m3_2 = add(S(Z), S(S(Z)))
summon[m3_2.type <:< S[S[S[Z]]]] // Error before changes: Cannot prove that (m3_2 : S[S[Nat]]) <:< S[S[S[Z]]]

val m4_2 = add(S(Z), S(S(S(Z))))
summon[m4_2.type <:< S[S[S[S[Z]]]]]


0 comments on commit cce2933

Please sign in to comment.