Skip to content

Commit e2aaf80

Browse files
committed
Merge pull request scala#2625 from paulp/pr/no-useless-implicits
SI-6899, prohibit dangerous, useless implicit conversions.
2 parents 319c260 + 2f0e5ec commit e2aaf80

File tree

8 files changed

+54
-66
lines changed

8 files changed

+54
-66
lines changed

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,15 @@ trait Implicits {
144144
private val infoMapCache = new LinkedHashMap[Symbol, InfoMap]
145145
private val improvesCache = perRunCaches.newMap[(ImplicitInfo, ImplicitInfo), Boolean]()
146146

147+
private def isInvalidConversionTarget(tpe: Type): Boolean = tpe match {
148+
case Function1(_, out) => AnyRefClass.tpe <:< out
149+
case _ => false
150+
}
151+
private def isInvalidConversionSource(tpe: Type): Boolean = tpe match {
152+
case Function1(in, _) => in <:< NullClass.tpe
153+
case _ => false
154+
}
155+
147156
def resetImplicits() {
148157
implicitsCache.clear()
149158
infoMapCache.clear()
@@ -1357,10 +1366,10 @@ trait Implicits {
13571366

13581367
val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits
13591368
result = materializeImplicit(pt)
1360-
13611369
// `materializeImplicit` does some preprocessing for `pt`
13621370
// is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`?
1363-
if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, isLocal = false)
1371+
if (result.isFailure && !wasAmbigious)
1372+
result = searchImplicit(implicitsOfExpectedType, isLocal = false)
13641373

13651374
if (result.isFailure) {
13661375
context.updateBuffer(previousErrs)
@@ -1370,9 +1379,18 @@ trait Implicits {
13701379
if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits)
13711380
}
13721381
}
1373-
1374-
if (result.isFailure && settings.debug)
1375-
log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
1382+
if (result.isSuccess && isView) {
1383+
if (isInvalidConversionTarget(pt)) {
1384+
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, "the result type of an implicit conversion must be more specific than AnyRef"))
1385+
result = SearchFailure
1386+
}
1387+
else if (isInvalidConversionSource(pt)) {
1388+
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree, "an expression of type Null is ineligible for implicit conversion"))
1389+
result = SearchFailure
1390+
}
1391+
}
1392+
if (result.isFailure)
1393+
debuglog("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
13761394

13771395
result
13781396
}

src/library/scala/Option.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ sealed abstract class Option[+A] extends Product with Serializable {
128128
* val textField = new JComponent(initalText.orNull,20)
129129
* }}}
130130
*/
131-
@inline final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null
131+
@inline final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse ev(null)
132132

133133
/** Returns a $some containing the result of applying $f to this $option's
134134
* value if this $option is nonempty.
@@ -210,7 +210,7 @@ sealed abstract class Option[+A] extends Product with Serializable {
210210
}
211211

212212
/** Tests whether the option contains a given value as an element.
213-
*
213+
*
214214
* @param elem the element to test.
215215
* @return `true` if the option has an element that is equal (as
216216
* determined by `==`) to `elem`, `false` otherwise.

src/library/scala/Predef.scala

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -346,19 +346,6 @@ object Predef extends LowPriorityImplicits with DeprecatedPredef {
346346
implicit def double2Double(x: Double) = java.lang.Double.valueOf(x)
347347
implicit def boolean2Boolean(x: Boolean) = java.lang.Boolean.valueOf(x)
348348

349-
// These next eight implicits exist solely to exclude AnyRef methods from the
350-
// eight implicits above so that primitives are not coerced to AnyRefs. They
351-
// only create such conflict for AnyRef methods, so the methods on the java.lang
352-
// boxed types are unambiguously reachable.
353-
implicit def byte2ByteConflict(x: Byte) = new AnyRef
354-
implicit def short2ShortConflict(x: Short) = new AnyRef
355-
implicit def char2CharacterConflict(x: Char) = new AnyRef
356-
implicit def int2IntegerConflict(x: Int) = new AnyRef
357-
implicit def long2LongConflict(x: Long) = new AnyRef
358-
implicit def float2FloatConflict(x: Float) = new AnyRef
359-
implicit def double2DoubleConflict(x: Double) = new AnyRef
360-
implicit def boolean2BooleanConflict(x: Boolean) = new AnyRef
361-
362349
implicit def Byte2byte(x: java.lang.Byte): Byte = x.byteValue
363350
implicit def Short2short(x: java.lang.Short): Short = x.shortValue
364351
implicit def Character2char(x: java.lang.Character): Char = x.charValue
@@ -481,24 +468,6 @@ private[scala] abstract class LowPriorityImplicits {
481468
@inline implicit def doubleWrapper(x: Double) = new runtime.RichDouble(x)
482469
@inline implicit def booleanWrapper(x: Boolean) = new runtime.RichBoolean(x)
483470

484-
// These eight implicits exist solely to exclude Null from the domain of
485-
// the boxed types, so that e.g. "var x: Int = null" is a compile time
486-
// error rather than a delayed null pointer exception by way of the
487-
// conversion from java.lang.Integer. If defined in the same template as
488-
// Integer2int, they would have higher priority because Null is a subtype
489-
// of Integer. We balance that out and create conflict by moving the
490-
// definition into the superclass.
491-
//
492-
// Caution: do not adjust tightrope tension without safety goggles in place.
493-
implicit def Byte2byteNullConflict(x: Null): Byte = sys.error("value error")
494-
implicit def Short2shortNullConflict(x: Null): Short = sys.error("value error")
495-
implicit def Character2charNullConflict(x: Null): Char = sys.error("value error")
496-
implicit def Integer2intNullConflict(x: Null): Int = sys.error("value error")
497-
implicit def Long2longNullConflict(x: Null): Long = sys.error("value error")
498-
implicit def Float2floatNullConflict(x: Null): Float = sys.error("value error")
499-
implicit def Double2doubleNullConflict(x: Null): Double = sys.error("value error")
500-
implicit def Boolean2booleanNullConflict(x: Null): Boolean = sys.error("value error")
501-
502471
implicit def genericWrapArray[T](xs: Array[T]): WrappedArray[T] =
503472
if (xs eq null) null
504473
else WrappedArray.make(xs)

test/files/neg/no-implicit-to-anyref.check

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
no-implicit-to-anyref.scala:11: error: type mismatch;
2-
found : Int(1)
3-
required: AnyRef
4-
Note: an implicit exists from scala.Int => java.lang.Integer, but
5-
methods inherited from Object are rendered ambiguous. This is to avoid
6-
a blanket implicit which would convert any scala.Int to any AnyRef.
7-
You may wish to use a type ascription: `x: java.lang.Integer`.
1+
no-implicit-to-anyref.scala:11: error: the result type of an implicit conversion must be more specific than AnyRef
82
1: AnyRef
93
^
104
no-implicit-to-anyref.scala:17: error: type mismatch;

test/files/neg/t4158.check

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
1-
t4158.scala:3: error: type mismatch;
2-
found : Null(null)
3-
required: Int
4-
Note that implicit conversions are not applicable because they are ambiguous:
5-
both method Integer2intNullConflict in class LowPriorityImplicits of type (x: Null)Int
6-
and method Integer2int in object Predef of type (x: Integer)Int
7-
are possible conversion functions from Null(null) to Int
1+
t4158.scala:3: error: an expression of type Null is ineligible for implicit conversion
82
var y = null: Int
93
^
10-
t4158.scala:2: error: type mismatch;
11-
found : Null(null)
12-
required: Int
13-
Note that implicit conversions are not applicable because they are ambiguous:
14-
both method Integer2intNullConflict in class LowPriorityImplicits of type (x: Null)Int
15-
and method Integer2int in object Predef of type (x: Integer)Int
16-
are possible conversion functions from Null(null) to Int
4+
t4158.scala:2: error: an expression of type Null is ineligible for implicit conversion
175
var x: Int = null
186
^
197
two errors found

test/files/neg/t4727.check

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
t4727.scala:5: error: type mismatch;
2-
found : Null
3-
required: Int
4-
Note that implicit conversions are not applicable because they are ambiguous:
5-
both method Integer2intNullConflict in class LowPriorityImplicits of type (x: Null)Int
6-
and method Integer2int in object Predef of type (x: Integer)Int
7-
are possible conversion functions from Null to Int
1+
t4727.scala:5: error: an expression of type Null is ineligible for implicit conversion
82
Error occurred in an application involving default arguments.
93
new C[Int]
104
^

test/files/neg/t6889.check

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

test/files/neg/t6889.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package bippy {
2+
trait Bippy[A] extends Any
3+
}
4+
package foo {
5+
package object unrelated {
6+
implicit def bippyDingo[A](x: bippy.Bippy[A]): AnyRef = Nil
7+
}
8+
package unrelated {
9+
trait Unrelated
10+
}
11+
}
12+
13+
object Test {
14+
trait Dingo extends Any with bippy.Bippy[foo.unrelated.Unrelated]
15+
16+
def f(x: Dingo): AnyRef = x // fail - no conversion to AnyRef
17+
var x: Int = null // fail - no conversion from Null
18+
}

0 commit comments

Comments
 (0)