Skip to content

Commit 7ff971f

Browse files
authored
Merge pull request #5886 from dotty-staging/change-implicit-conversions-2
Fix implicit conversion warnings
2 parents a198e48 + bb8d358 commit 7ff971f

File tree

5 files changed

+100
-19
lines changed

5 files changed

+100
-19
lines changed

compiler/src/dotty/tools/dotc/typer/Checking.scala

+17-11
Original file line numberDiff line numberDiff line change
@@ -634,17 +634,23 @@ trait Checking {
634634
* - it is defined in Predef
635635
* - it is the scala.reflect.Selectable.reflectiveSelectable conversion
636636
*/
637-
def checkImplicitConversionUseOK(sym: Symbol, posd: Positioned)(implicit ctx: Context): Unit = {
638-
val conversionOK =
639-
!sym.exists ||
640-
sym.is(Synthetic) ||
641-
sym.info.finalResultType.classSymbols.exists(_.owner.isLinkedWith(sym.owner)) ||
642-
defn.isPredefClass(sym.owner) ||
643-
sym.name == nme.reflectiveSelectable && sym.maybeOwner.maybeOwner.maybeOwner == defn.ScalaPackageClass
644-
if (!conversionOK)
645-
checkFeature(defn.LanguageModuleClass, nme.implicitConversions,
646-
i"Use of implicit conversion ${sym.showLocated}", NoSymbol, posd.sourcePos)
647-
}
637+
def checkImplicitConversionUseOK(sym: Symbol, posd: Positioned)(implicit ctx: Context): Unit =
638+
if (sym.exists) {
639+
val conv =
640+
if (sym.is(Implicit)) sym
641+
else {
642+
assert(sym.name == nme.apply)
643+
sym.owner
644+
}
645+
val conversionOK =
646+
conv.is(Synthetic) ||
647+
sym.info.finalResultType.classSymbols.exists(_.isLinkedWith(conv.owner)) ||
648+
defn.isPredefClass(conv.owner) ||
649+
conv.name == nme.reflectiveSelectable && conv.maybeOwner.maybeOwner.maybeOwner == defn.ScalaPackageClass
650+
if (!conversionOK)
651+
checkFeature(defn.LanguageModuleClass, nme.implicitConversions,
652+
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, posd.sourcePos)
653+
}
648654

649655
/** Issue a feature warning if feature is not enabled */
650656
def checkFeature(base: ClassSymbol,

compiler/test/dotty/tools/dotc/CompilationTests.scala

+2
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ class CompilationTests extends ParallelTesting {
149149
compileFilesInDir("tests/neg-custom-args/fatal-warnings", defaultOptions.and("-Xfatal-warnings")) +
150150
compileFilesInDir("tests/neg-custom-args/allow-double-bindings", allowDoubleBindings) +
151151
compileDir("tests/neg-custom-args/impl-conv", defaultOptions.and("-Xfatal-warnings", "-feature")) +
152+
compileFile("tests/neg-custom-args/implicit-conversions.scala", defaultOptions.and("-Xfatal-warnings", "-feature")) +
153+
compileFile("tests/neg-custom-args/implicit-conversions-old.scala", defaultOptions.and("-Xfatal-warnings", "-feature")) +
152154
compileFile("tests/neg-custom-args/i3246.scala", scala2Mode) +
153155
compileFile("tests/neg-custom-args/overrideClass.scala", scala2Mode) +
154156
compileFile("tests/neg-custom-args/autoTuplingTest.scala", defaultOptions.and("-language:noAutoTupling")) +

library/src/scalaShadowing/language.scala

+29-8
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ object language {
9292
*/
9393
@volatile implicit lazy val reflectiveCalls: reflectiveCalls = languageFeature.reflectiveCalls
9494

95-
/** Only where enabled, definitions of implicit conversions are allowed. An
96-
* implicit conversion is an implicit value of unary function type `A => B`,
95+
/** Only where enabled, definitions of legacy implicit conversions and certain uses
96+
* of implicit conversions are allowed.
97+
*
98+
* A legacy implicit conversion is an implicit value of unary function type `A => B`,
9799
* or an implicit method that has in its first parameter section a single,
98100
* non-implicit parameter. Examples:
99101
*
@@ -103,17 +105,36 @@ object language {
103105
* implicit def listToX(xs: List[T])(implicit f: T => X): X = ...
104106
* }}}
105107
*
106-
* implicit values of other types are not affected, and neither are implicit
107-
* classes.
108+
* Implicit values of other types are not affected, and neither are implicit
109+
* classes. In particular, implied instances of the scala.Conversion class can be
110+
* defined without having to import the language feature.
111+
*
112+
* The language import is also required to enable _uses_ of implicit conversions
113+
* unless the conversion in question is co-defined with the type to which it maps.
114+
* Co-defined means: defined in the companion object of the class of the result type.
115+
* Examples:
116+
*
117+
* {{{
118+
* class A
119+
* class B
120+
* object B {
121+
* implied a2b for Conversion[A, B] { ... }
122+
* }
123+
* object C {
124+
* implied b2a for Conversion[B, A] { ... }
125+
* }
126+
* import implied B._
127+
* import implied C._
128+
* val x: A = new B // language import required
129+
* val x: B = new A // no import necessary since a2b is co-defined with B
130+
* }}}
108131
*
109132
* '''Why keep the feature?''' Implicit conversions are central to many aspects
110133
* of Scala’s core libraries.
111134
*
112135
* '''Why control it?''' Implicit conversions are known to cause many pitfalls
113-
* if over-used. And there is a tendency to over-use them because they look
114-
* very powerful and their effects seem to be easy to understand. Also, in
115-
* most situations using implicit parameters leads to a better design than
116-
* implicit conversions.
136+
* if over-used. This holds in particular for implicit conversions defined after
137+
* the fact between unrelated types.
117138
*
118139
* @group production
119140
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class A
2+
class B
3+
4+
object A {
5+
6+
implicit def a2b(x: A): B = ??? // error under -Xfatal-warnings -feature
7+
8+
implicit def b2a(x: B): A = ??? // error under -Xfatal-warnings -feature
9+
}
10+
11+
class C
12+
13+
object D {
14+
implicit def a2c(x: A): C = ??? // error under -Xfatal-warnings -feature
15+
}
16+
17+
object Test {
18+
import D._
19+
20+
val x1: A = new B
21+
val x2: B = new A // error under -Xfatal-warnings -feature
22+
val x3: C = new A // error under -Xfatal-warnings -feature
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class A
2+
class B
3+
4+
object A {
5+
6+
implied for Conversion[A, B] {
7+
def apply(x: A): B = ???
8+
}
9+
10+
implied for Conversion[B, A] {
11+
def apply(x: B): A = ???
12+
}
13+
}
14+
15+
class C
16+
17+
object D {
18+
implied for Conversion[A, C] {
19+
def apply(x: A): C = ???
20+
}
21+
}
22+
23+
object Test {
24+
import implied D._
25+
26+
val x1: A = new B
27+
val x2: B = new A // error under -Xfatal-warnings -feature
28+
val x3: C = new A // error under -Xfatal-warnings -feature
29+
}

0 commit comments

Comments
 (0)