From 686a1e401f351d061c53f7241d9acab19c2180cf Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 6 Mar 2020 15:36:34 +0100 Subject: [PATCH] Always exclude package objects from the implicit scope Unlike Scala 2, in Dotty when constructing the implicit scope for a type `pkgA.Foo`, we do not include the main package object (`pkgA.package`). We don't include the package objects we define for top-level definitions either. However, when constructing the implicit scope for a type defined in a package object (e.g. `pkgA.foo$package.Foo`), we did including the implicits defined in the enclosing package object. This is problematic because at the source-level it's hard to distinguish which definitions will be included in the package object. For example, in the testcase included in this commit, `summon[ToString[A.AB]]` used to succeed because both the type `AB` and the given alias for `ToString[AB]` ended up wrapped in a package object, but the other summon calls failed because classes and given instances are not wrapped in a package object. To fix this inconsistency, we now always exclude package objects from the implicit scope, even for types defined in the package objects itself. The companion object of classes and opaque types stays the preferred place to put implicit definitions and is not affected by this change. --- .../dotty/tools/dotc/typer/Implicits.scala | 6 +-- tests/neg/implicit-package-object.scala | 45 +++++++++++++++++++ .../pos/toplevel-opaque-xm/Logarithm_1.scala | 13 +++--- tests/pos/toplevel-opaque-xm/Test_2.scala | 9 +--- 4 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 tests/neg/implicit-package-object.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 32c4a2eb3be9..9f546dc896ca 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -507,10 +507,10 @@ trait ImplicitRunInfo { val incomplete: mutable.Set[Type] = mutable.Set() /** Is `sym` an anchor type for which givens may exist? Anchor types are classes, - * opaque type aliases, and abstract types, but not type parameters + * opaque type aliases, and abstract types, but not type parameters or package objects. */ def isAnchor(sym: Symbol) = - sym.isClass && !sym.is(Package) + sym.isClass && !sym.is(Package) && (!sym.isPackageObject || ctx.scala2CompatMode) || sym.isOpaqueAlias || sym.is(Deferred, butNot = Param) @@ -584,7 +584,7 @@ trait ImplicitRunInfo { addPath(pre.prefix) } } - else { + else if (!pre.symbol.isPackageObject || ctx.scala2CompatMode) { comps += pre addPath(pre.prefix) } diff --git a/tests/neg/implicit-package-object.scala b/tests/neg/implicit-package-object.scala new file mode 100644 index 000000000000..deb276c94c05 --- /dev/null +++ b/tests/neg/implicit-package-object.scala @@ -0,0 +1,45 @@ +trait ToString[A] { + def print(a: A): Unit +} + +package A { + case class AA(text: String) + given ToString[AA] = aa => println(aa.text) + + opaque type AB = String + given ToString[AB] = ab => println(ab) + + opaque type AC = String + given ToString[AC] { + def print(ac: AC): Unit = println(ac) + } +} + +package B { + case class BA(text: String) + object BA { + given ToString[BA] = ba => println(ba.text) + } + + opaque type BB = String + object BB { + given ToString[BB] = bb => println(bb) + } + + opaque type BC = String + object BC { + given ToString[BC] { + def print(bc: BC): Unit = println(bc) + } + } +} + +object Test { + val AA = summon[ToString[A.AA]] // error + val AB = summon[ToString[A.AB]] // error, used to compile + val AC = summon[ToString[A.AC]] // error + + val BA = summon[ToString[B.BA]] + val BB = summon[ToString[B.BB]] + val BC = summon[ToString[B.BC]] +} diff --git a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala index c9e4e439713b..884e29a1ad25 100644 --- a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala +++ b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala @@ -2,7 +2,7 @@ package logs opaque type Logarithm = Double -implicit object Logarithm { +object Logarithm { // These are the ways to lift to the logarithm type def apply(d: Double): Logarithm = math.log(d) @@ -13,10 +13,11 @@ implicit object Logarithm { // This is the first way to unlift the logarithm type def exponent(l: Logarithm): Double = l - // Extension methods define opaque types' public APIs - // This is the second way to unlift the logarithm type - def (x: Logarithm).toDouble: Double = math.exp(x) - def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) - def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) + given AnyRef { + // This is the second way to unlift the logarithm type + def (x: Logarithm).toDouble: Double = math.exp(x) + def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) + def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) + } } diff --git a/tests/pos/toplevel-opaque-xm/Test_2.scala b/tests/pos/toplevel-opaque-xm/Test_2.scala index b0e61aa41aaa..6ff16cd03c30 100644 --- a/tests/pos/toplevel-opaque-xm/Test_2.scala +++ b/tests/pos/toplevel-opaque-xm/Test_2.scala @@ -1,15 +1,10 @@ package logs -import Predef.{any2stringadd => _, _} - object Test { val l = Logarithm(1.0) val l2 = Logarithm(2.0) val l3 = l * l2 - val l4 = l + l2 // currently requires any2stringadd to be disabled because - // as a contextual implicit this takes precedence over the - // implicit scope implicit LogarithmOps. - // TODO: Remove any2stringadd - val d = Logarithm.toDouble(l3) + val l4 = l + l2 + val d = l3.toDouble val l5: Logarithm = (1.0).asInstanceOf[Logarithm] }