Skip to content

Commit 9d25000

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] ``` (cherry picked from commit 68799b9)
1 parent aac607b commit 9d25000

File tree

3 files changed

+38
-6
lines changed

3 files changed

+38
-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
@@ -617,17 +617,31 @@ trait Implicits {
617617
/** This expresses more cleanly in the negative: there's a linear path
618618
* to a final true or false.
619619
*/
620-
private def isPlausiblySubType(tp1: Type, tp2: Type) = !isImpossibleSubType(tp1, tp2)
621-
private def isImpossibleSubType(tp1: Type, tp2: Type) = tp1.dealiasWiden match {
620+
private def isPlausiblySubType(tp1: Type, tp2: Type): Boolean = !isImpossibleSubType(tp1, tp2)
621+
private def isImpossibleSubType(tp1: Type, tp2: Type): Boolean = tp1.dealiasWiden match {
622622
// We can only rule out a subtype relationship if the left hand
623623
// side is a class, else we may not know enough.
624-
case tr1 @ TypeRef(_, sym1, _) if sym1.isClass =>
624+
case tr1 @ TypeRef(_, sym1, args1) if sym1.isClass =>
625625
def typeRefHasMember(tp: TypeRef, name: Name) = {
626626
tp.baseClasses.exists(_.info.decls.lookupEntry(name) != null)
627627
}
628-
tp2.dealiasWiden match {
629-
case TypeRef(_, sym2, _) => ((sym1 eq ByNameParamClass) != (sym2 eq ByNameParamClass)) || (sym2.isClass && !(sym1 isWeakSubClass sym2))
630-
case RefinedType(parents, decls) => decls.nonEmpty && !typeRefHasMember(tr1, decls.head.name) // opt avoid full call to .member
628+
629+
def existentialUnderlying(t: Type) = t match {
630+
case et: ExistentialType => et.underlying
631+
case tp => tp
632+
}
633+
val tp2Bounds = existentialUnderlying(tp2.dealiasWiden.bounds.hi)
634+
tp2Bounds match {
635+
case TypeRef(_, sym2, args2) if sym2 ne SingletonClass =>
636+
val impossible = if ((sym1 eq sym2) && (args1 ne Nil)) !corresponds3(sym1.typeParams, args1, args2) {(tparam, arg1, arg2) =>
637+
if (tparam.isCovariant) isPlausiblySubType(arg1, arg2) else isPlausiblySubType(arg2, arg1)
638+
} else {
639+
((sym1 eq ByNameParamClass) != (sym2 eq ByNameParamClass)) || (sym2.isClass && !(sym1 isWeakSubClass sym2))
640+
}
641+
impossible
642+
case RefinedType(parents, decls) =>
643+
val impossible = decls.nonEmpty && !typeRefHasMember(tr1, decls.head.name) // opt avoid full call to .member
644+
impossible
631645
case _ => false
632646
}
633647
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+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import language.implicitConversions
2+
3+
class Test {
4+
implicit def singletonToString(c: Singleton): String = ""
5+
def foo(a: 1): String = a // implicit was being ruled out because Int(1).widen was not a subclass of Singletom
6+
}

0 commit comments

Comments
 (0)