Skip to content

Commit 45f7ef6

Browse files
committed
Constrain by deepening results in more implicit searches
We used to look at deep expected type when the result of an implicit was ambiguous. This could add more constraints which could resolve the ambiguity. We now do the same also if the search type has uninstantiated type variables. In that case, consulting more context might further constrain type variables, which might in turn enlarge the implicit scope so that a solution can be found. Fixes #23526
1 parent e6bf7b8 commit 45f7ef6

File tree

4 files changed

+35
-13
lines changed

4 files changed

+35
-13
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
3030

3131
val synthesizedClassTag: SpecialHandler = (formal, span) =>
3232
def instArg(tp: Type): Type = tp.dealias match
33-
// Special case to avoid instantiating `Int & S` to `Int & Nothing` in
34-
// i16328.scala. The intersection comes from an earlier instantiation
35-
// to an upper bound.
36-
// The dual situation with unions is harder to trigger because lower
37-
// bounds are usually widened during instantiation.
3833
case tp: AndOrType if tp.tp1 =:= tp.tp2 =>
34+
// Special case to avoid instantiating `Int & S` to `Int & Nothing` in
35+
// i16328.scala. The intersection comes from an earlier instantiation
36+
// to an upper bound.
37+
// The dual situation with unions is harder to trigger because lower
38+
// bounds are usually widened during instantiation.
3939
instArg(tp.tp1)
4040
case tvar: TypeVar if ctx.typerState.constraint.contains(tvar) =>
4141
instArg(

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4361,12 +4361,23 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
43614361
implicitArgs(formals2, argIndex + 1, pt)
43624362

43634363
val arg = inferImplicitArg(formal, tree.span.endPos)
4364+
4365+
def canProfitFromMoreConstraints =
4366+
arg.tpe.isInstanceOf[AmbiguousImplicits]
4367+
// ambiguity could be decided by more constraints
4368+
|| !isFullyDefined(formal, ForceDegree.none)
4369+
// more context might constrain type variables which could make implicit scope larger
4370+
43644371
arg.tpe match
4365-
case failed: AmbiguousImplicits =>
4372+
case failed: SearchFailureType if canProfitFromMoreConstraints =>
43664373
val pt1 = pt.deepenProtoTrans
43674374
if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && constrainResult(tree.symbol, wtp, pt1)
4368-
then implicitArgs(formals, argIndex, pt1)
4369-
else arg :: implicitArgs(formals1, argIndex + 1, pt1)
4375+
then return implicitArgs(formals, argIndex, pt1)
4376+
case _ =>
4377+
4378+
arg.tpe match
4379+
case failed: AmbiguousImplicits =>
4380+
arg :: implicitArgs(formals1, argIndex + 1, pt)
43704381
case failed: SearchFailureType =>
43714382
lazy val defaultArg = findDefaultArgument(argIndex)
43724383
.showing(i"default argument: for $formal, $tree, $argIndex = $result", typr)

tests/neg/i9568.check

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
| No given instance of type => Monad[F] was found for parameter ev of method blaMonad in object Test.
55
| I found:
66
|
7-
| Test.blaMonad[F², S](Test.blaMonad[F³, S²])
7+
| Test.blaMonad[F², S]
88
|
9-
| But method blaMonad in object Test does not match type => Monad[F²]
9+
| But method blaMonad in object Test does not match type => Monad[F]
1010
|
1111
| where: F is a type variable with constraint <: [_] =>> Any
1212
| F² is a type variable with constraint <: [_] =>> Any
13-
| F³ is a type variable with constraint <: [_] =>> Any
14-
| S is a type variable
15-
| S² is a type variable
1613
| .

tests/pos/i23526.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait B[-A, +To] {
2+
def addOne(e: A): this.type = this
3+
def res(): To
4+
}
5+
6+
class Col[A]
7+
8+
object Factory {
9+
def newB[A](using reflect.ClassTag[A]) = new B[A, Col[A]] { def res(): Col[A] = new Col[A] }
10+
}
11+
12+
def test =
13+
val a = Factory.newB.addOne(1).res()
14+
val b = collection.immutable.ArraySeq.newBuilder.addOne(1).result()

0 commit comments

Comments
 (0)