Skip to content

Commit 7c6ee6b

Browse files
committed
Change bounded wildcard import syntax
It's now `_: Type` instead of `for Type` and it works for all kinds of imports. There are use cases for normal imports as well. E.g. ``` import Color.{_: Color} ``` to get just the enum cases and nothing else.
1 parent 16c2434 commit 7c6ee6b

File tree

7 files changed

+61
-18
lines changed

7 files changed

+61
-18
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,12 +2388,18 @@ object Parsers {
23882388

23892389
/** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’
23902390
* FinalSelector ::= ImportSelector
2391-
* | ‘_’
2392-
* | ‘for’ InfixType {‘,’ InfixType}
2391+
* | ‘_’ [‘:’ Type]
23932392
*/
23942393
def importSelectors(): List[Tree] = in.token match {
23952394
case USCORE =>
2396-
wildcardIdent() :: Nil
2395+
atSpan(in.skipToken()) {
2396+
val id = Ident(nme.WILDCARD)
2397+
if (in.token == COLON) {
2398+
in.nextToken()
2399+
TypeBoundsTree(EmptyTree, typ())
2400+
}
2401+
else id
2402+
} :: Nil
23972403
case FOR =>
23982404
if (!importDelegate)
23992405
syntaxError(em"`for` qualifier only allowed in `import given`")

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,26 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
9191
myForwardMapping = myForwardMapping.updated(name, name)
9292
myReverseMapping = myReverseMapping.updated(name, name)
9393
case TypeBoundsTree(_, tpt) =>
94-
myWildcardImport = true // details are handled separately in impliedBounds
94+
myWildcardImport = true // details are handled separately in wildcardBounds
9595
}
9696
recur(sels1)
9797
case nil =>
9898
}
9999
recur(selectors)
100100
}
101101

102-
private[this] var myDelegateBound: Type = null
102+
private[this] var myWildcardBound: Type = null
103103

104-
def impliedBound(implicit ctx: Context): Type = {
105-
if (myDelegateBound == null)
106-
myDelegateBound = selectors.lastOption match {
104+
def wildcardBound(implicit ctx: Context): Type = {
105+
if (myWildcardBound == null)
106+
myWildcardBound = selectors.lastOption match {
107107
case Some(TypeBoundsTree(_, untpd.TypedSplice(tpt))) => tpt.tpe
108108
case Some(TypeBoundsTree(_, tpt)) =>
109-
myDelegateBound = NoType
109+
myWildcardBound = NoType
110110
ctx.typer.typedAheadType(tpt).tpe
111111
case _ => NoType
112112
}
113-
myDelegateBound
113+
myWildcardBound
114114
}
115115

116116
private def implicitFlags(implicit ctx: Context) =
@@ -128,8 +128,8 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
128128
val renamed = forwardMapping(ref.name)
129129
if (renamed == ref.name) ref :: Nil
130130
else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil
131-
else if (!impliedBound.exists ||
132-
normalizedCompatible(ref, impliedBound, keepConstraint = false)) ref :: Nil
131+
else if (!wildcardBound.exists ||
132+
normalizedCompatible(ref, wildcardBound, keepConstraint = false)) ref :: Nil
133133
else Nil
134134
}
135135
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ class Typer extends Namer
189189
var excl = EmptyFlags
190190
if (imp.importDelegate) reqd |= Delegate else excl |= Delegate
191191
var denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx)
192-
if (checkBounds && imp.impliedBound.exists)
192+
if (checkBounds && imp.wildcardBound.exists)
193193
denot = denot.filterWithPredicate(mbr =>
194-
NoViewsAllowed.normalizedCompatible(mbr.info, imp.impliedBound, keepConstraint = false))
194+
NoViewsAllowed.normalizedCompatible(mbr.info, imp.wildcardBound, keepConstraint = false))
195195

196196
// Pass refctx so that any errors are reported in the context of the
197197
// reference instead of the
@@ -234,7 +234,7 @@ class Typer extends Namer
234234
*/
235235
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type =
236236
if (imp.isWildcardImport && !imp.excluded.contains(name.toTermName) && name != nme.CONSTRUCTOR)
237-
selection(imp, name, checkBounds = imp.importDelegate)
237+
selection(imp, name, checkBounds = true)
238238
else NoType
239239

240240
/** Is (some alternative of) the given predenotation `denot`

docs/docs/internals/syntax.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,7 @@ Import ::= ‘import’ [‘given’] ImportExpr {‘,’ ImportExpr
340340
ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) Import(expr, sels)
341341
ImportSelectors ::= ‘{’ {ImportSelector ‘,’} FinalSelector ‘}’
342342
FinalSelector ::= ImportSelector Ident(name)
343-
| ‘_’ Pair(id, id)
344-
| ‘for’ InfixType {‘,’ InfixType} TypeBoundsTree(EmptyTree, tpt)
343+
| ‘_’ [‘:’ Type] TypeBoundsTree(EmptyTree, tpt)
345344
ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’]
346345
Export ::= ‘export’ [‘given’] ImportExpr {‘,’ ImportExpr}
347346
```

docs/docs/reference/contextual/delegates.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ given IntOrd as Ord[Int] {
1919
}
2020

2121
given ListOrd[T] as Ord[List[T]] given (ord: Ord[T]) {
22-
where Ord[T], { x <= 0 }
2322

2423
def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match {
2524
case (Nil, Nil) => 0

docs/docs/reference/contextual/import-delegate.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ import given Instances.{im, _: Ordering[_]}
5858
```
5959
would import `im`, `intOrd`, and `listOrd` but leave out `ec`. By-type imports cannot be mixed with a wildcard import in the same import clause.
6060

61+
Bounded wildcard selectors also work for normal imports and exports. For instance, consider the following `enum` definition:
62+
```scala
63+
enum Color {
64+
case Red, Green, Blue, Magenta
65+
66+
def isPrimary(c: Color): Boolean = ...
67+
}
68+
export Color.{_: Color}
69+
```
70+
The export clause makes all four three `Color` values available as unqualified constants, but
71+
leaves the `isPrimary` method alone.
72+
6173
### Migration
6274

6375
The rules for imports given above have the consequence that a library

tests/run/boundedImports.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class C[X]
2+
3+
object A extends C[String] {
4+
5+
val a: A.type = this
6+
val b: String = ""
7+
val c: Integer = 10
8+
9+
}
10+
11+
enum Color {
12+
case Red, Green, Blue
13+
14+
def identity(x: Any): Any = ""
15+
}
16+
17+
object Test extends App {
18+
val c = 0
19+
20+
import A.{_: C[_] | String}
21+
assert(a eq A)
22+
assert(b == "")
23+
assert(c == 0)
24+
25+
import Color.{_: Color}
26+
assert(identity("hello") == "hello")
27+
}

0 commit comments

Comments
 (0)