Skip to content

Commit c84b370

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 e3df10a commit c84b370

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
@@ -626,17 +626,31 @@ trait Implicits {
626626
/** This expresses more cleanly in the negative: there's a linear path
627627
* to a final true or false.
628628
*/
629-
private def isPlausiblySubType(tp1: Type, tp2: Type) = !isImpossibleSubType(tp1, tp2)
630-
private def isImpossibleSubType(tp1: Type, tp2: Type) = tp1.dealiasWiden match {
629+
private def isPlausiblySubType(tp1: Type, tp2: Type): Boolean = !isImpossibleSubType(tp1, tp2)
630+
private def isImpossibleSubType(tp1: Type, tp2: Type): Boolean = tp1.dealiasWiden match {
631631
// We can only rule out a subtype relationship if the left hand
632632
// side is a class, else we may not know enough.
633-
case tr1 @ TypeRef(_, sym1, _) if sym1.isClass =>
633+
case tr1 @ TypeRef(_, sym1, args1) if sym1.isClass =>
634634
def typeRefHasMember(tp: TypeRef, name: Name) = {
635635
tp.baseClasses.exists(_.info.decls.lookupEntry(name) != null)
636636
}
637-
tp2.dealiasWiden match {
638-
case TypeRef(_, sym2, _) => ((sym1 eq ByNameParamClass) != (sym2 eq ByNameParamClass)) || (sym2.isClass && !(sym1 isWeakSubClass sym2))
639-
case RefinedType(parents, decls) => decls.nonEmpty && !typeRefHasMember(tr1, decls.head.name) // opt avoid full call to .member
637+
638+
def existentialUnderlying(t: Type) = t match {
639+
case et: ExistentialType => et.underlying
640+
case tp => tp
641+
}
642+
val tp2Bounds = existentialUnderlying(tp2.dealiasWiden.bounds.hi)
643+
tp2Bounds match {
644+
case TypeRef(_, sym2, args2) =>
645+
val impossible = if ((sym1 eq sym2) && (args1 ne Nil)) !corresponds3(sym1.typeParams, args1, args2) {(tparam, arg1, arg2) =>
646+
if (tparam.isCovariant) isPlausiblySubType(arg1, arg2) else isPlausiblySubType(arg2, arg1)
647+
} else {
648+
((sym1 eq ByNameParamClass) != (sym2 eq ByNameParamClass)) || (sym2.isClass && !(sym1 isWeakSubClass sym2))
649+
}
650+
impossible
651+
case RefinedType(parents, decls) =>
652+
val impossible = decls.nonEmpty && !typeRefHasMember(tr1, decls.head.name) // opt avoid full call to .member
653+
impossible
640654
case _ => false
641655
}
642656
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)