Skip to content

Commit 8345078

Browse files
authored
Merge pull request #10949 from dotty-staging/fix-10857
fix #10857: export from wildcard defs with same name as user declared ones
2 parents d049102 + 7ebdc1c commit 8345078

File tree

4 files changed

+89
-12
lines changed

4 files changed

+89
-12
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,15 +1086,24 @@ class Namer { typer: Typer =>
10861086
}
10871087
}
10881088

1089+
def addWildcardForwardersNamed(name: TermName, span: Span): Unit =
1090+
List(name, name.toTypeName)
1091+
.flatMap(path.tpe.memberBasedOnFlags(_, excluded = Private|Given|ConstructorProxy).alternatives)
1092+
.foreach(addForwarder(name, _, span)) // ignore if any are not added
1093+
10891094
def addWildcardForwarders(seen: List[TermName], span: Span): Unit =
1095+
val nonContextual = mutable.HashSet(seen: _*)
10901096
for mbr <- path.tpe.membersBasedOnFlags(required = EmptyFlags, excluded = PrivateOrSynthetic) do
10911097
if !mbr.symbol.isSuperAccessor then
10921098
// Scala 2 superaccessors have neither Synthetic nor Artfact set, so we
10931099
// need to filter them out here (by contrast, Scala 3 superaccessors are Artifacts)
10941100
val alias = mbr.name.toTermName
1095-
if !seen.contains(alias)
1096-
&& mbr.matchesImportBound(if mbr.symbol.is(Given) then givenBound else wildcardBound)
1097-
then addForwarder(alias, mbr, span)
1101+
if mbr.symbol.is(Given) then
1102+
if !seen.contains(alias) && mbr.matchesImportBound(givenBound) then
1103+
addForwarder(alias, mbr, span)
1104+
else if !nonContextual.contains(alias) && mbr.matchesImportBound(wildcardBound) then
1105+
nonContextual += alias
1106+
addWildcardForwardersNamed(alias, span)
10981107

10991108
def addForwarders(sels: List[untpd.ImportSelector], seen: List[TermName]): Unit = sels match
11001109
case sel :: sels1 =>

docs/docs/reference/other-new-features/export.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ An export clause has the same format as an import clause. Its general form is:
4747

4848
```scala
4949
export path . { sel_1, ..., sel_n }
50-
export given path . { sel_1, ..., sel_n }
5150
```
5251

5352
It consists of a qualifier expression `path`, which must be a stable identifier, followed by
@@ -58,7 +57,8 @@ of one of the following forms:
5857
- A _renaming selector_ `x => y` creates aliases for all eligible members of `path` that are named `x`, but the alias is named `y` instead of `x`.
5958
- An _omitting selector_ `x => _` prevents `x` from being aliased by a subsequent
6059
wildcard selector.
61-
- A _wildcard selector_ creates aliases for all eligible members of `path` except for
60+
- A _given selector_ `given x` has an optional type bound `x`. It creates aliases for all eligible given instances that conform to either `x`, or `Any` if `x` is omitted, except for members that are named by a previous simple, renaming, or omitting selector.
61+
- A _wildcard selector_ `_` creates aliases for all eligible members of `path` except for given instances,
6262
synthetic members generated by the compiler and those members that are named by a previous simple, renaming, or omitting selector.
6363

6464
A member is _eligible_ if all of the following holds:
@@ -68,8 +68,7 @@ A member is _eligible_ if all of the following holds:
6868
a base class of the class containing the export clause.
6969
- it is accessible at the export clause,
7070
- it is not a constructor, nor the (synthetic) class part of an object,
71-
- it is a given instance (or an old-style `implicit` value)
72-
if and only if the export is tagged with `given`.
71+
- it is a given instance (declared with `given`) if and only if the export is from a _given selector_.
7372

7473
It is a compile-time error if a simple or renaming selector does not identify any eligible
7574
members.
@@ -95,6 +94,8 @@ def f: c.T = ...
9594
<a id="note_class"></a>
9695
Export clauses can appear in classes or they can appear at the top-level. An export clause cannot appear as a statement in a block.
9796

97+
If an export clause contains a wildcard or given selector, it is forbidden for its qualifier path to refer to a package. This is because it is not yet known how to safely track wildcard dependencies to a package for the purposes of incremental compilation.
98+
9899
(\*) **Note:** Unless otherwise stated, the term "class" in this discussion also includes object and trait definitions.
99100

100101
## Motivation
@@ -111,11 +112,21 @@ more flexible way.
111112
## Syntax changes:
112113

113114
```
114-
TemplateStat ::= ...
115-
| Export
116-
TopStat ::= ...
117-
| Export
118-
Export ::= ‘export’ [‘given’] ImportExpr {‘,’ ImportExpr}
115+
TemplateStat ::= ...
116+
| Export
117+
TopStat ::= ...
118+
| Export
119+
Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
120+
ImportExpr ::= StableId ‘.’ ImportSpec
121+
ImportSpec ::= id
122+
| ‘_’
123+
| ‘given’
124+
| ‘{’ ImportSelectors) ‘}’
125+
ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
126+
| WildCardSelector {‘,’ WildCardSelector}
127+
WildCardSelector ::= ‘_'
128+
| ‘given’ [InfixType]
129+
Export ::= ‘export’ ImportExpr {‘,’ ImportExpr}
119130
```
120131

121132
## Elaboration of Export Clauses

tests/neg/i10857.scala

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
object Module:
2+
3+
class Bar
4+
class Baz
5+
class Qux
6+
7+
object Givens:
8+
given GivenBar: Bar = new Bar()
9+
def GivenBar(ignored: Int): Bar = new Bar()
10+
class GivenBar
11+
12+
object Members:
13+
given Member: Baz = new Baz()
14+
private def Member(ignored1: String)(ignored2: Int): Bar = new Bar()
15+
def Member(ignored: Int): Baz = new Baz()
16+
class Member
17+
18+
object Combined:
19+
given GivenQux: Qux = new Qux()
20+
def GivenQux(ignored: Int): Qux = new Qux()
21+
22+
enum Color:
23+
case Red, Green, Blue
24+
25+
export Color._ // will only export synthetic defs with same name as standard definition
26+
export Givens.given // should only export given values
27+
export Members._ // should only export values that are not given
28+
export Combined.{_, given} // should only export values that are not given
29+
30+
@main def Test =
31+
32+
println(Module.Red)
33+
println(Module.valueOf("Red")) // error: value valueOf is not a member
34+
35+
println(summon[Module.Bar])
36+
println(new Module.GivenBar()) // error: type GivenBar is not a member
37+
println(Module.GivenBar(23)) // error: method GivenBar does not take parameters
38+
39+
println(new Module.Member())
40+
println(Module.Member(23))
41+
println(Module.Member("?")(23)) // error: Found: ("?" : String) Required: Int
42+
println(summon[Module.Baz]) // error: no implicit argument of type Module.Baz was found
43+
44+
println(summon[Module.Qux])
45+
println(Module.GivenQux(23))

tests/run/i10857.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object Module:
2+
3+
enum Foo:
4+
case Value
5+
case Parameterised(i: Int)
6+
7+
export Foo._
8+
9+
@main def Test =
10+
import Module.given
11+
println(Module.Parameterised.apply(23)) // synthetic companion object is exported
12+
println(Module.Value)

0 commit comments

Comments
 (0)