Skip to content

Commit 99f41a1

Browse files
authored
Merge pull request scala#5736 from adriaanm/t10206
SI-10206 tighten fix for SI-6889
2 parents 363d9e5 + 849d09b commit 99f41a1

File tree

4 files changed

+43
-21
lines changed

4 files changed

+43
-21
lines changed

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

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,6 @@ trait Implicits {
134134
private val improvesCache = perRunCaches.newMap[(ImplicitInfo, ImplicitInfo), Boolean]()
135135
private val implicitSearchId = { var id = 1 ; () => try id finally id += 1 }
136136

137-
private def isInvalidConversionSource(tpe: Type): Boolean = tpe match {
138-
case Function1(in, _) => in <:< NullClass.tpe
139-
case _ => false
140-
}
141-
142137
def resetImplicits() {
143138
implicitsCache.clear()
144139
infoMapCache.clear()
@@ -1388,27 +1383,32 @@ trait Implicits {
13881383
}
13891384
}
13901385
if (result.isSuccess && isView) {
1391-
def maybeInvalidConversionError(msg: String) {
1386+
def maybeInvalidConversionError(msg: String): Boolean = {
13921387
// We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError"
13931388
// which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690.
13941389
// AM: I would guess it's because ambiguous errors will be buffered in silent mode if they are not reported
13951390
if (context.ambiguousErrors)
13961391
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg))
1392+
true
13971393
}
13981394
pt match {
1399-
case Function1(_, out) =>
1400-
// must inline to avoid capturing result
1401-
def prohibit(sym: Symbol) = (sym.tpe <:< out) && {
1402-
maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}")
1403-
true
1404-
}
1405-
if (prohibit(AnyRefClass) || (settings.isScala211 && prohibit(AnyValClass)))
1406-
result = SearchFailure
1407-
case _ => false
1408-
}
1409-
if (settings.isScala211 && isInvalidConversionSource(pt)) {
1410-
maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion")
1411-
result = SearchFailure
1395+
// SI-10206 don't use subtyping to rule out AnyRef/AnyVal:
1396+
// - there are several valid structural types that are supertypes of AnyRef (e.g., created by HasMember);
1397+
// typeSymbol will do the trick (AnyRef is a type alias for Object), while ruling out these structural types
1398+
// - also don't want to accidentally constrain type vars through using <:<
1399+
case Function1(in, out) =>
1400+
val outSym = out.typeSymbol
1401+
1402+
val fail =
1403+
if (out.annotations.isEmpty && (outSym == ObjectClass || (isScala211 && outSym == AnyValClass)))
1404+
maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than $out")
1405+
else if (isScala211 && in.annotations.isEmpty && in.typeSymbol == NullClass)
1406+
maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion")
1407+
else false
1408+
1409+
if (fail) result = SearchFailure
1410+
1411+
case _ =>
14121412
}
14131413
}
14141414

@@ -1418,6 +1418,9 @@ trait Implicits {
14181418
result
14191419
}
14201420

1421+
// this setting is expensive to check, actually....
1422+
private[this] val isScala211 = settings.isScala211
1423+
14211424
def allImplicits: List[SearchResult] = {
14221425
def search(iss: Infoss, isLocalToCallsite: Boolean) = applicableInfos(iss, isLocalToCallsite).values
14231426
(

test/files/neg/t6889.check

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
t6889.scala:16: error: the result type of an implicit conversion must be more specific than AnyRef
22
def f(x: Dingo): AnyRef = x // fail - no conversion to AnyRef
33
^
4-
t6889.scala:17: error: an expression of type Null is ineligible for implicit conversion
4+
t6889.scala:17: error: the result type of an implicit conversion must be more specific than Object
5+
def f2(x: Dingo): Object = x // fail - no conversion to Object
6+
^
7+
t6889.scala:18: error: an expression of type Null is ineligible for implicit conversion
58
var x: Int = null // fail - no conversion from Null
69
^
7-
two errors found
10+
three errors found

test/files/neg/t6889.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ object Test {
1414
trait Dingo extends Any with bippy.Bippy[foo.unrelated.Unrelated]
1515

1616
def f(x: Dingo): AnyRef = x // fail - no conversion to AnyRef
17+
def f2(x: Dingo): Object = x // fail - no conversion to Object
1718
var x: Int = null // fail - no conversion from Null
1819
}

test/files/pos/t10206.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Foo(val bar: String)
2+
3+
object Foo {
4+
implicit class Enrich(foo: Foo) {
5+
def clone(x: Int, y: Int): Int = x + y
6+
}
7+
}
8+
9+
object Main extends App {
10+
val foo = new Foo("hello")
11+
println(foo.clone(1, 2)) // <- does not compile
12+
// the implicit view was being disqualified because a new check in the compiler
13+
// that implicit views must not target Any or AnyRef considered an implicit search
14+
// for `foo.type => ?{def clone: ?}` to targeted AnyRef.
15+
}

0 commit comments

Comments
 (0)