Skip to content

Commit 73b935c

Browse files
authored
Refine implicit search fallbacks for better ClassTag handling (#23532)
1. Don't use FullyDefinedType when synthesizing ClassTags It's not necessary to instantiate all type variables, deeply, since we are only interested in the outermost shape. Also, that way we do not instantiate to Nothing, which is something difficult to recover from. This step is necessary to enable (2) 2. Constrain by deepening expected result types in more implicit searches We used to look at the 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
2 parents fb66af2 + 45f7ef6 commit 73b935c

File tree

5 files changed

+45
-17
lines changed

5 files changed

+45
-17
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,9 @@ object Inferencing {
445445
}
446446

447447
/** The instantiation decision for given poly param computed from the constraint. */
448-
enum Decision { case Min; case Max; case ToMax; case Skip; case Fail }
448+
enum Decision:
449+
case Min, Max, ToMax, Skip, Fail
450+
449451
private def instDecision(tvar: TypeVar, v: Int, minimizeSelected: Boolean, ifBottom: IfBottom)(using Context): Decision =
450452
import Decision.*
451453
val direction = instDirection(tvar.origin)

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,21 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
2929
private type SpecialHandlers = List[(ClassSymbol, SpecialHandler)]
3030

3131
val synthesizedClassTag: SpecialHandler = (formal, span) =>
32-
def instArg(tp: Type): Type = tp.stripTypeVar 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.
32+
def instArg(tp: Type): Type = tp.dealias match
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)
40+
case tvar: TypeVar if ctx.typerState.constraint.contains(tvar) =>
41+
instArg(
42+
if tvar.hasLowerBound then tvar.instantiate(fromBelow = true)
43+
else if tvar.hasUpperBound then tvar.instantiate(fromBelow = false)
44+
else NoType)
4045
case _ =>
41-
if isFullyDefined(tp, ForceDegree.all) then tp
42-
else NoType // this happens in tests/neg/i15372.scala
46+
tp
4347

4448
val tag = formal.argInfos match
4549
case arg :: Nil =>

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

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

43604360
val arg = inferImplicitArg(formal, tree.span.endPos)
4361+
4362+
def canProfitFromMoreConstraints =
4363+
arg.tpe.isInstanceOf[AmbiguousImplicits]
4364+
// ambiguity could be decided by more constraints
4365+
|| !isFullyDefined(formal, ForceDegree.none)
4366+
// more context might constrain type variables which could make implicit scope larger
4367+
43614368
arg.tpe match
4362-
case failed: AmbiguousImplicits =>
4369+
case failed: SearchFailureType if canProfitFromMoreConstraints =>
43634370
val pt1 = pt.deepenProtoTrans
43644371
if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && constrainResult(tree.symbol, wtp, pt1)
4365-
then implicitArgs(formals, argIndex, pt1)
4366-
else arg :: implicitArgs(formals1, argIndex + 1, pt1)
4372+
then return implicitArgs(formals, argIndex, pt1)
4373+
case _ =>
4374+
4375+
arg.tpe match
4376+
case failed: AmbiguousImplicits =>
4377+
arg :: implicitArgs(formals1, argIndex + 1, pt)
43674378
case failed: SearchFailureType =>
43684379
lazy val defaultArg = findDefaultArgument(argIndex)
43694380
.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)