diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 520c8bf62ba4..6f5dc86193a7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -444,7 +444,9 @@ object Inferencing { } /** The instantiation decision for given poly param computed from the constraint. */ - enum Decision { case Min; case Max; case ToMax; case Skip; case Fail } + enum Decision: + case Min, Max, ToMax, Skip, Fail + private def instDecision(tvar: TypeVar, v: Int, minimizeSelected: Boolean, ifBottom: IfBottom)(using Context): Decision = import Decision.* val direction = instDirection(tvar.origin) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 761a24e10474..cb3bba1cf0e4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -29,17 +29,21 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): private type SpecialHandlers = List[(ClassSymbol, SpecialHandler)] val synthesizedClassTag: SpecialHandler = (formal, span) => - def instArg(tp: Type): Type = tp.stripTypeVar match - // Special case to avoid instantiating `Int & S` to `Int & Nothing` in - // i16328.scala. The intersection comes from an earlier instantiation - // to an upper bound. - // The dual situation with unions is harder to trigger because lower - // bounds are usually widened during instantiation. + def instArg(tp: Type): Type = tp.dealias match case tp: AndOrType if tp.tp1 =:= tp.tp2 => + // Special case to avoid instantiating `Int & S` to `Int & Nothing` in + // i16328.scala. The intersection comes from an earlier instantiation + // to an upper bound. + // The dual situation with unions is harder to trigger because lower + // bounds are usually widened during instantiation. instArg(tp.tp1) + case tvar: TypeVar if ctx.typerState.constraint.contains(tvar) => + instArg( + if tvar.hasLowerBound then tvar.instantiate(fromBelow = true) + else if tvar.hasUpperBound then tvar.instantiate(fromBelow = false) + else NoType) case _ => - if isFullyDefined(tp, ForceDegree.all) then tp - else NoType // this happens in tests/neg/i15372.scala + tp val tag = formal.argInfos match case arg :: Nil => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 819a66b52946..d33cf0883268 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4361,12 +4361,23 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer implicitArgs(formals2, argIndex + 1, pt) val arg = inferImplicitArg(formal, tree.span.endPos) + + def canProfitFromMoreConstraints = + arg.tpe.isInstanceOf[AmbiguousImplicits] + // ambiguity could be decided by more constraints + || !isFullyDefined(formal, ForceDegree.none) + // more context might constrain type variables which could make implicit scope larger + arg.tpe match - case failed: AmbiguousImplicits => + case failed: SearchFailureType if canProfitFromMoreConstraints => val pt1 = pt.deepenProtoTrans if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && constrainResult(tree.symbol, wtp, pt1) - then implicitArgs(formals, argIndex, pt1) - else arg :: implicitArgs(formals1, argIndex + 1, pt1) + then return implicitArgs(formals, argIndex, pt1) + case _ => + + arg.tpe match + case failed: AmbiguousImplicits => + arg :: implicitArgs(formals1, argIndex + 1, pt) case failed: SearchFailureType => lazy val defaultArg = findDefaultArgument(argIndex) .showing(i"default argument: for $formal, $tree, $argIndex = $result", typr) diff --git a/tests/neg/i9568.check b/tests/neg/i9568.check index 3f318d0b0111..744023714a69 100644 --- a/tests/neg/i9568.check +++ b/tests/neg/i9568.check @@ -4,13 +4,10 @@ | No given instance of type => Monad[F] was found for parameter ev of method blaMonad in object Test. | I found: | - | Test.blaMonad[F², S](Test.blaMonad[F³, S²]) + | Test.blaMonad[F², S] | - | But method blaMonad in object Test does not match type => Monad[F²] + | But method blaMonad in object Test does not match type => Monad[F] | | where: F is a type variable with constraint <: [_] =>> Any | F² is a type variable with constraint <: [_] =>> Any - | F³ is a type variable with constraint <: [_] =>> Any - | S is a type variable - | S² is a type variable | . diff --git a/tests/pos/i23526.scala b/tests/pos/i23526.scala new file mode 100644 index 000000000000..e530608435c7 --- /dev/null +++ b/tests/pos/i23526.scala @@ -0,0 +1,14 @@ +trait B[-A, +To] { + def addOne(e: A): this.type = this + def res(): To +} + +class Col[A] + +object Factory { + def newB[A](using reflect.ClassTag[A]) = new B[A, Col[A]] { def res(): Col[A] = new Col[A] } +} + +def test = + val a = Factory.newB.addOne(1).res() + val b = collection.immutable.ArraySeq.newBuilder.addOne(1).result()