Skip to content

Commit

Permalink
Merge pull request scala#5736 from adriaanm/t10206
Browse files Browse the repository at this point in the history
SI-10206 tighten fix for SI-6889
  • Loading branch information
adriaanm authored Mar 21, 2017
2 parents 363d9e5 + 849d09b commit 99f41a1
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 21 deletions.
41 changes: 22 additions & 19 deletions src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,6 @@ trait Implicits {
private val improvesCache = perRunCaches.newMap[(ImplicitInfo, ImplicitInfo), Boolean]()
private val implicitSearchId = { var id = 1 ; () => try id finally id += 1 }

private def isInvalidConversionSource(tpe: Type): Boolean = tpe match {
case Function1(in, _) => in <:< NullClass.tpe
case _ => false
}

def resetImplicits() {
implicitsCache.clear()
infoMapCache.clear()
Expand Down Expand Up @@ -1388,27 +1383,32 @@ trait Implicits {
}
}
if (result.isSuccess && isView) {
def maybeInvalidConversionError(msg: String) {
def maybeInvalidConversionError(msg: String): Boolean = {
// We have to check context.ambiguousErrors even though we are calling "issueAmbiguousError"
// which ostensibly does exactly that before issuing the error. Why? I have no idea. Test is pos/t7690.
// AM: I would guess it's because ambiguous errors will be buffered in silent mode if they are not reported
if (context.ambiguousErrors)
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, msg))
true
}
pt match {
case Function1(_, out) =>
// must inline to avoid capturing result
def prohibit(sym: Symbol) = (sym.tpe <:< out) && {
maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than ${sym.name}")
true
}
if (prohibit(AnyRefClass) || (settings.isScala211 && prohibit(AnyValClass)))
result = SearchFailure
case _ => false
}
if (settings.isScala211 && isInvalidConversionSource(pt)) {
maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion")
result = SearchFailure
// SI-10206 don't use subtyping to rule out AnyRef/AnyVal:
// - there are several valid structural types that are supertypes of AnyRef (e.g., created by HasMember);
// typeSymbol will do the trick (AnyRef is a type alias for Object), while ruling out these structural types
// - also don't want to accidentally constrain type vars through using <:<
case Function1(in, out) =>
val outSym = out.typeSymbol

val fail =
if (out.annotations.isEmpty && (outSym == ObjectClass || (isScala211 && outSym == AnyValClass)))
maybeInvalidConversionError(s"the result type of an implicit conversion must be more specific than $out")
else if (isScala211 && in.annotations.isEmpty && in.typeSymbol == NullClass)
maybeInvalidConversionError("an expression of type Null is ineligible for implicit conversion")
else false

if (fail) result = SearchFailure

case _ =>
}
}

Expand All @@ -1418,6 +1418,9 @@ trait Implicits {
result
}

// this setting is expensive to check, actually....
private[this] val isScala211 = settings.isScala211

def allImplicits: List[SearchResult] = {
def search(iss: Infoss, isLocalToCallsite: Boolean) = applicableInfos(iss, isLocalToCallsite).values
(
Expand Down
7 changes: 5 additions & 2 deletions test/files/neg/t6889.check
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
t6889.scala:16: error: the result type of an implicit conversion must be more specific than AnyRef
def f(x: Dingo): AnyRef = x // fail - no conversion to AnyRef
^
t6889.scala:17: error: an expression of type Null is ineligible for implicit conversion
t6889.scala:17: error: the result type of an implicit conversion must be more specific than Object
def f2(x: Dingo): Object = x // fail - no conversion to Object
^
t6889.scala:18: error: an expression of type Null is ineligible for implicit conversion
var x: Int = null // fail - no conversion from Null
^
two errors found
three errors found
1 change: 1 addition & 0 deletions test/files/neg/t6889.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ object Test {
trait Dingo extends Any with bippy.Bippy[foo.unrelated.Unrelated]

def f(x: Dingo): AnyRef = x // fail - no conversion to AnyRef
def f2(x: Dingo): Object = x // fail - no conversion to Object
var x: Int = null // fail - no conversion from Null
}
15 changes: 15 additions & 0 deletions test/files/pos/t10206.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class Foo(val bar: String)

object Foo {
implicit class Enrich(foo: Foo) {
def clone(x: Int, y: Int): Int = x + y
}
}

object Main extends App {
val foo = new Foo("hello")
println(foo.clone(1, 2)) // <- does not compile
// the implicit view was being disqualified because a new check in the compiler
// that implicit views must not target Any or AnyRef considered an implicit search
// for `foo.type => ?{def clone: ?}` to targeted AnyRef.
}

0 comments on commit 99f41a1

Please sign in to comment.