Skip to content

Commit 9464bf0

Browse files
authored
Make SIP 54 (Multi-Source Extension Overloads) a standard feature (#17441)
- Drop experimental language import - Change note in docs to say that this relaxation only applies to extension method calls, not extension methods called as normal methods. I tried to also reflect the second point in error messages but it turned out too hard. At the point where we generate the error message we do not know how the method was called and it would be unsystematic to create that side channel. In fact, information flows the other way: When we resolve an extension method name, we buffer the error messages and fix selected AmbiguityErrors.
2 parents c14d2de + 81b4e5c commit 9464bf0

File tree

12 files changed

+58
-62
lines changed

12 files changed

+58
-62
lines changed

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ object Feature:
2929
val fewerBraces = experimental("fewerBraces")
3030
val saferExceptions = experimental("saferExceptions")
3131
val clauseInterleaving = experimental("clauseInterleaving")
32-
val relaxedExtensionImports = experimental("relaxedExtensionImports")
3332
val pureFunctions = experimental("pureFunctions")
3433
val captureChecking = experimental("captureChecking")
3534
val into = experimental("into")

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,7 +1395,8 @@ class ConstrProxyShadows(proxy: TermRef, shadowed: Type, shadowedIsApply: Boolea
13951395
|or use a full prefix for ${shadowed.termSymbol.name} if you mean the latter."""
13961396
end ConstrProxyShadows
13971397

1398-
class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context)(using Context)
1398+
class AmbiguousReference(
1399+
name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context, isExtension: => Boolean = false)(using Context)
13991400
extends ReferenceMsg(AmbiguousReferenceID), NoDisambiguation {
14001401

14011402
/** A string which explains how something was bound; Depending on `prec` this is either
@@ -1417,10 +1418,17 @@ class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec
14171418
i"""$howVisible$qualifier in ${whereFound.owner}"""
14181419
}
14191420

1421+
def importHint =
1422+
if (newPrec == BindingPrec.NamedImport || newPrec == BindingPrec.WildImport)
1423+
&& prevPrec == newPrec
1424+
&& isExtension
1425+
then i"\n\n Hint: This error may arise if extension method `$name` is called as a normal method."
1426+
else ""
1427+
14201428
def msg(using Context) =
14211429
i"""|Reference to $name is ambiguous.
14221430
|It is both ${bindingString(newPrec, ctx)}
1423-
|and ${bindingString(prevPrec, prevCtx, " subsequently")}"""
1431+
|and ${bindingString(prevPrec, prevCtx, " subsequently")}$importHint"""
14241432

14251433
def explain(using Context) =
14261434
val precedent =

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
237237
found
238238
else
239239
if !scala2pkg && !previous.isError && !found.isError then
240-
fail(AmbiguousReference(name, newPrec, prevPrec, prevCtx))
240+
fail(AmbiguousReference(name, newPrec, prevPrec, prevCtx,
241+
isExtension = previous.termSymbol.is(ExtensionMethod) && found.termSymbol.is(ExtensionMethod)))
241242
previous
242243

243244
/** Assemble and check alternatives to an imported reference. This implies:
@@ -264,7 +265,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
264265
then
265266
altImports.uncheckedNN += altImp
266267

267-
if Feature.enabled(Feature.relaxedExtensionImports) && altImports != null && ctx.isImportContext then
268+
if altImports != null && ctx.isImportContext then
268269
val curImport = ctx.importInfo.uncheckedNN
269270
namedImportRef(curImport) match
270271
case altImp: TermRef =>

docs/_docs/reference/contextual/extension-methods.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ The following two rewritings are tried in order:
254254
not a wildcard import, pick the expansion from that import. Otherwise, report
255255
an ambiguous reference error.
256256

257-
**Note**: This relaxation is currently enabled only under the `experimental.relaxedExtensionImports` language import.
257+
**Note**: This relaxation of the import rules applies only if the method `m` is used as an extension method. If it is used as a normal method in prefix form, the usual import rules apply, which means that importing `m` from
258+
multiple places can lead to an ambiguity error.
258259

259260
2. If the first rewriting does not typecheck with expected type `T`,
260261
and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.m[Ts](e)`. An object `o` is _eligible_ if

library/src/scala/runtime/stdLibPatches/language.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,6 @@ object language:
6969
@compileTimeOnly("`clauseInterleaving` can only be used at compile time in import statements")
7070
object clauseInterleaving
7171

72-
/** Adds support for relaxed imports of extension methods.
73-
* Extension methods with the same name can be imported from several places.
74-
*
75-
* @see [[http://dotty.epfl.ch/docs/reference/contextual/extension-methods]]
76-
*/
77-
@compileTimeOnly("`relaxedExtensionImports` can only be used at compile time in import statements")
78-
object relaxedExtensionImports
79-
8072
/** Experimental support for pure function type syntax
8173
*
8274
* @see [[https://dotty.epfl.ch/docs/reference/experimental/purefuns]]

tests/neg/i13558.scala

Lines changed: 0 additions & 31 deletions
This file was deleted.

tests/neg/i16920.check

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
-- [E008] Not Found Error: tests/neg/i16920.scala:20:11 ----------------------------------------------------------------
2-
20 | "five".wow // error
1+
-- [E008] Not Found Error: tests/neg/i16920.scala:19:11 ----------------------------------------------------------------
2+
19 | "five".wow // error
33
| ^^^^^^^^^^
44
| value wow is not a member of String.
55
| An extension method was tried, but could not be fully constructed:
@@ -10,8 +10,8 @@
1010
|
1111
| Found: ("five" : String)
1212
| Required: Int
13-
-- [E008] Not Found Error: tests/neg/i16920.scala:28:6 -----------------------------------------------------------------
14-
28 | 5.wow // error
13+
-- [E008] Not Found Error: tests/neg/i16920.scala:27:6 -----------------------------------------------------------------
14+
27 | 5.wow // error
1515
| ^^^^^
1616
| value wow is not a member of Int.
1717
| An extension method was tried, but could not be fully constructed:
@@ -22,8 +22,8 @@
2222
|
2323
| Found: (5 : Int)
2424
| Required: Boolean
25-
-- [E008] Not Found Error: tests/neg/i16920.scala:29:11 ----------------------------------------------------------------
26-
29 | "five".wow // error
25+
-- [E008] Not Found Error: tests/neg/i16920.scala:28:11 ----------------------------------------------------------------
26+
28 | "five".wow // error
2727
| ^^^^^^^^^^
2828
| value wow is not a member of String.
2929
| An extension method was tried, but could not be fully constructed:
@@ -34,8 +34,8 @@
3434
|
3535
| Found: ("five" : String)
3636
| Required: Boolean
37-
-- [E008] Not Found Error: tests/neg/i16920.scala:36:6 -----------------------------------------------------------------
38-
36 | 5.wow // error
37+
-- [E008] Not Found Error: tests/neg/i16920.scala:35:6 -----------------------------------------------------------------
38+
35 | 5.wow // error
3939
| ^^^^^
4040
| value wow is not a member of Int.
4141
| An extension method was tried, but could not be fully constructed:
@@ -48,8 +48,8 @@
4848
| both Three.wow(5)
4949
| and Two.wow(5)
5050
| are possible expansions of 5.wow
51-
-- [E008] Not Found Error: tests/neg/i16920.scala:44:11 ----------------------------------------------------------------
52-
44 | "five".wow // error
51+
-- [E008] Not Found Error: tests/neg/i16920.scala:43:11 ----------------------------------------------------------------
52+
43 | "five".wow // error
5353
| ^^^^^^^^^^
5454
| value wow is not a member of String.
5555
| An extension method was tried, but could not be fully constructed:
@@ -60,8 +60,8 @@
6060
|
6161
| Found: ("five" : String)
6262
| Required: Int
63-
-- [E008] Not Found Error: tests/neg/i16920.scala:51:11 ----------------------------------------------------------------
64-
51 | "five".wow // error
63+
-- [E008] Not Found Error: tests/neg/i16920.scala:50:11 ----------------------------------------------------------------
64+
50 | "five".wow // error
6565
| ^^^^^^^^^^
6666
| value wow is not a member of String.
6767
| An extension method was tried, but could not be fully constructed:
@@ -72,8 +72,8 @@
7272
|
7373
| Found: ("five" : String)
7474
| Required: Int
75-
-- [E008] Not Found Error: tests/neg/i16920.scala:58:6 -----------------------------------------------------------------
76-
58 | 5.wow // error
75+
-- [E008] Not Found Error: tests/neg/i16920.scala:57:6 -----------------------------------------------------------------
76+
57 | 5.wow // error
7777
| ^^^^^
7878
| value wow is not a member of Int.
7979
| An extension method was tried, but could not be fully constructed:

tests/neg/i16920.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import language.experimental.relaxedExtensionImports
21

32
object One:
43
extension (s: String)

tests/neg/sip54.check

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-- [E049] Reference Error: tests/neg/sip54.scala:12:8 ------------------------------------------------------------------
2+
12 |val _ = meth(foo)() // error // error
3+
| ^^^^
4+
| Reference to meth is ambiguous.
5+
| It is both imported by import A._
6+
| and imported subsequently by import B._
7+
|
8+
| Hint: This error may arise if extension method `meth` is called as a normal method.
9+
|
10+
| longer explanation available when compiling with `-explain`
11+
-- [E007] Type Mismatch Error: tests/neg/sip54.scala:12:13 -------------------------------------------------------------
12+
12 |val _ = meth(foo)() // error // error
13+
| ^^^
14+
| Found: (foo : Foo)
15+
| Required: Bar
16+
|
17+
| longer explanation available when compiling with `-explain`

tests/neg/sip54.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Foo
2+
class Bar
3+
object A:
4+
extension (foo: Foo) def meth(): Foo = foo
5+
object B:
6+
extension (bar: Bar) def meth(): Bar = bar
7+
8+
import A.*
9+
import B.*
10+
11+
val foo = new Foo
12+
val _ = meth(foo)() // error // error

tests/pos/i13558.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
package testcode
2-
import language.experimental.relaxedExtensionImports
32

43
class A
54

tests/pos/i16920.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import language.experimental.relaxedExtensionImports
21

32
object One:
43
extension (s: String)

0 commit comments

Comments
 (0)