Skip to content

Commit 59f29cf

Browse files
committed
Rule out more implicit based on bounds of type parameters
Before: ``` implicitly[Foo[String]] BYVALmode-EXPRmode (site: value <local Test> in Test) |-- implicitly BYVALmode-EXPRmode-FUNmode-POLYmode-TAPPmode (site: value <local Test> in Test) | \-> [T](implicit e: T)T |-- Foo[String] TYPEmode (site: value <local Test> in Test) | |-- String TYPEmode (site: value <local Test> in Test) | | [adapt] String is now a TypeTree(String) | | \-> String | \-> Foo[String] [search #1] start `[T](implicit e: T)T`, searching for adaptation to pt=Foo[String] (silent: value <local Test> in Test) implicits disabled [search #1] considering Foo.javaEnumFoo solving for (T: ?T) [adapt] [T]=> Foo[T] adapted to [T]=> Foo[T] based on pt Foo[String] [search #1] success inferred value of type Foo[String] is SearchResult(Foo.javaEnumFoo[String], ) |-- [T](implicit e: T)T BYVALmode-EXPRmode (site: value <local Test> in Test) | \-> Foo[String] [adapt] [T](implicit e: T)T adapted to [T](implicit e: T)T ``` After: ``` implicitly[Foo[String]] BYVALmode-EXPRmode (site: value <local Test> in Test) |-- implicitly BYVALmode-EXPRmode-FUNmode-POLYmode-TAPPmode (site: value <local Test> in Test) | \-> [T](implicit e: T)T |-- Foo[String] TYPEmode (site: value <local Test> in Test) | |-- String TYPEmode (site: value <local Test> in Test) | | [adapt] String is now a TypeTree(String) | | \-> String | \-> Foo[String] [search #1] start `[T](implicit e: T)T`, searching for adaptation to pt=Foo[String] (silent: value <local Test> in Test) implicits disabled [search #1] considering Foo.stringFoo [search #1] success inferred value of type Foo[String] is SearchResult(Foo.stringFoo, ) |-- [T](implicit e: T)T BYVALmode-EXPRmode (site: value <local Test> in Test) | \-> Foo[String] [adapt] [T](implicit e: T)T adapted to [T](implicit e: T)T \-> Foo[String] ```
1 parent 3a30e57 commit 59f29cf

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

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

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -683,17 +683,31 @@ trait Implicits {
683683
/** This expresses more cleanly in the negative: there's a linear path
684684
* to a final true or false.
685685
*/
686-
private def isPlausiblySubType(tp1: Type, tp2: Type) = !isImpossibleSubType(tp1, tp2)
687-
private def isImpossibleSubType(tp1: Type, tp2: Type) = tp1.dealiasWiden match {
686+
private def isPlausiblySubType(tp1: Type, tp2: Type): Boolean = !isImpossibleSubType(tp1, tp2)
687+
private def isImpossibleSubType(tp1: Type, tp2: Type): Boolean = tp1.dealiasWiden match {
688688
// We can only rule out a subtype relationship if the left hand
689689
// side is a class, else we may not know enough.
690-
case tr1 @ TypeRef(_, sym1, _) if sym1.isClass =>
690+
case tr1 @ TypeRef(_, sym1, args1) if sym1.isClass =>
691691
def typeRefHasMember(tp: TypeRef, name: Name) = {
692692
tp.baseClasses.exists(_.info.decls.lookupEntry(name) != null)
693693
}
694-
tp2.dealiasWiden match {
695-
case TypeRef(_, sym2, _) => ((sym1 eq ByNameParamClass) != (sym2 eq ByNameParamClass)) || (sym2.isClass && !(sym1 isWeakSubClass sym2))
696-
case RefinedType(parents, decls) => decls.nonEmpty && !typeRefHasMember(tr1, decls.head.name) // opt avoid full call to .member
694+
695+
def existentialUnderlying(t: Type) = t match {
696+
case et: ExistentialType => et.underlying
697+
case tp => tp
698+
}
699+
val tp2Bounds = existentialUnderlying(tp2.dealiasWiden.bounds.hi) // TODO incompat with sip23-narrow.scala on 2.13.x
700+
tp2Bounds match {
701+
case TypeRef(_, sym2, args2) =>
702+
val impossible = if ((sym1 eq sym2) && (args1 ne Nil)) !corresponds3(sym1.typeParams, args1, args2) {(tparam, arg1, arg2) =>
703+
if (tparam.isCovariant) isPlausiblySubType(arg1, arg2) else isPlausiblySubType(arg2, arg1)
704+
} else {
705+
((sym1 eq ByNameParamClass) != (sym2 eq ByNameParamClass)) || (sym2.isClass && !(sym1 isWeakSubClass sym2))
706+
}
707+
impossible
708+
case RefinedType(parents, decls) =>
709+
val impossible = decls.nonEmpty && !typeRefHasMember(tr1, decls.head.name) // opt avoid full call to .member
710+
impossible
697711
case _ => false
698712
}
699713
case _ => false
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
trait Foo[T]
2+
object Foo {
3+
implicit def javaEnumFoo[T <: java.lang.Enum[_]]: Foo[T] = ???
4+
implicit def stringFoo: Foo[String] = ???
5+
}
6+
7+
object Test {
8+
// -Ytyper-debug output shows whether or not `javaEnumFoo` is considered
9+
// By making `isImpossibleSubtype` a little smarter, we can exclude it
10+
// on the grounds that `String` can't be a subtpe of the bounds ot `Enum[_]`.
11+
implicitly[Foo[String]]
12+
}

0 commit comments

Comments
 (0)