Skip to content

Commit dd31672

Browse files
committed
Use solved type vars to improve bounds of undetermined vars
When instantiating type parameters and retracting unsolved ones via adjustTypeArgs and friends we end up with a list of solved type params/variables and a list of still-undetermined type parameters. Typically this is followed by a substitution of the solved variables into the positions of the corresponding parameters, and type checking proceeds with the substitutions eliminated from the list of undetermined type parameters. However, the substituted parameters might occur as components of the bounds of the type parameters which are yet to be determined and, prior to this commit, these occurrences are not substituted into with the solved variables. This leads to issues of the form seen in scala/bug#10528. This commit performs the substitution similarly to the way it is done in enhanceBounds in typedAppliedTypeTree and fixes scala/bug#10528. The new addition is largely a duplicate of the existing mechanism in part to enable it to use updateInfo rather than setInfo and so preserve type histories. There is a TODO to investigate whether the original mechanism should also attempt to preserve type histories and whether the two methods can be combined.
1 parent c7de0eb commit dd31672

File tree

4 files changed

+55
-0
lines changed

4 files changed

+55
-0
lines changed

src/compiler/scala/tools/nsc/typechecker/Implicits.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ trait Implicits {
737737
// we must be conservative in leaving type params in undetparams
738738
// prototype == WildcardType: want to remove all inferred Nothings
739739
val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, tvars, targs)
740+
enhanceBounds(okParams, okArgs, undetParams)
740741

741742
val subst: TreeTypeSubstituter =
742743
if (okParams.isEmpty) EmptyTreeTypeSubstituter

src/compiler/scala/tools/nsc/typechecker/Infer.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,17 @@ trait Infer extends Checkable {
712712
argtpes
713713
}
714714

715+
// This is primarily a duplicte of enhanceBounds in typedAppliedTypeTree
716+
// modified to use updateInfo rather than setInfo to avoid wiping out
717+
// type history.
718+
def enhanceBounds(okparams: List[Symbol], okargs: List[Type], undets: List[Symbol]): Unit =
719+
undets.foreach { undet =>
720+
val bounds = undet.info.bounds
721+
val substBounds = bounds.subst(okparams, okargs)
722+
if(bounds ne substBounds)
723+
undet.updateInfo(substBounds)
724+
}
725+
715726
private def isApplicableToMethod(undetparams: List[Symbol], mt: MethodType, argtpes0: List[Type], pt: Type): Boolean = {
716727
val formals = formalTypes(mt.paramTypes, argtpes0.length, removeByName = false)
717728
def missingArgs = missingParams[Type](argtpes0, mt.params, x => Some(x) collect { case NamedType(n, _) => n })
@@ -728,6 +739,7 @@ trait Infer extends Checkable {
728739
def tryInstantiating(args: List[Type]) = falseIfNoInstance {
729740
val restpe = mt resultType args
730741
val AdjustedTypeArgs.Undets(okparams, okargs, leftUndet) = methTypeArgs(EmptyTree, undetparams, formals, restpe, args, pt)
742+
enhanceBounds(okparams, okargs, leftUndet)
731743
val restpeInst = restpe.instantiateTypeParams(okparams, okargs)
732744
// #2665: must use weak conformance, not regular one (follow the monomorphic case above)
733745
exprTypeArgs(leftUndet, restpeInst, pt, useWeaklyCompatible = true) match {
@@ -936,11 +948,14 @@ trait Infer extends Checkable {
936948
List()
937949
} else {
938950
val AdjustedTypeArgs.Undets(okParams, okArgs, leftUndet) = adjustTypeArgs(tparams, tvars, targsStrict)
951+
enhanceBounds(okParams, okArgs, leftUndet)
952+
939953
def solved_s = map2(okParams, okArgs)((p, a) => s"$p=$a") mkString ","
940954
def undet_s = leftUndet match {
941955
case Nil => ""
942956
case ps => ps.mkString(", undet=", ",", "")
943957
}
958+
944959
printTyping(tree, s"infer solved $solved_s$undet_s")
945960
substExpr(tree, okParams, okArgs, pt)
946961
leftUndet
@@ -982,6 +997,7 @@ trait Infer extends Checkable {
982997

983998
val AdjustedTypeArgs.AllArgsAndUndets(okparams, okargs, allargs, leftUndet) =
984999
methTypeArgs(fn, undetparams, formals, restpe, argtpes, pt)
1000+
enhanceBounds(okparams, okargs, leftUndet)
9851001

9861002
if (checkBounds(fn, NoPrefix, NoSymbol, undetparams, allargs, "inferred ")) {
9871003
val treeSubst = new TreeTypeSubstituter(okparams, okargs)

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5188,6 +5188,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
51885188
val asym = arg.symbol
51895189
def abounds = asym.info.bounds
51905190
def tbounds = tparam.info.bounds
5191+
// TODO investigate whether this should be merged with the near duplicate in Inferencer
5192+
// and whether or not we should avoid using setInfo here as well to avoid potentially
5193+
// trampling on type history.
51915194
def enhanceBounds(): Unit = {
51925195
val TypeBounds(lo0, hi0) = abounds
51935196
val TypeBounds(lo1, hi1) = tbounds.subst(tparams, argtypes)

test/files/pos/t10528.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
object Test {
2+
trait Holder[A]
3+
trait NilHolder[A] extends Holder[A]
4+
5+
trait Solve[A, H <: Holder[A]] {
6+
type Output <: Holder[A]
7+
}
8+
type SolveAux[A, H <: Holder[A], O <: Holder[A]] = Solve[A, H] {type Output = O}
9+
10+
implicit def nilSolve[A] = new Solve[A, NilHolder[A]] {
11+
override type Output = NilHolder[A]
12+
}
13+
14+
trait WrapSolve[A, H <: Holder[A]] {
15+
type Output <: Holder[A]
16+
}
17+
18+
implicit def wrapAux[A, H <: Holder[A], O <: Holder[A]](implicit one : SolveAux[A, H, O]) =
19+
new WrapSolve[A, H] {
20+
override type Output = O
21+
}
22+
23+
val wrapped = implicitly[WrapSolve[String, NilHolder[String]]]
24+
}
25+
26+
object Test2 {
27+
class Inv[T]
28+
class Foo[T, U <: Inv[T]]
29+
30+
implicit def foo[T]: Foo[T, Inv[T]] = new Foo[T, Inv[T]]
31+
32+
def bar[T, U <: Inv[T]](implicit foo: Foo[T, U]): Inv[T] = new Inv[T]
33+
34+
val baz: Inv[Int] = bar
35+
}

0 commit comments

Comments
 (0)