Skip to content

Commit b789e87

Browse files
committed
Implement for clauses for implied imports
1 parent 5bc95a0 commit b789e87

File tree

11 files changed

+150
-71
lines changed

11 files changed

+150
-71
lines changed

compiler/src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ object Denotations {
243243
*/
244244
def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation
245245

246+
override def filterWithPredicate(p: SingleDenotation => Boolean): Denotation
247+
246248
/** If this is a SingleDenotation, return it, otherwise throw a TypeError */
247249
def checkUnique(implicit ctx: Context): SingleDenotation = suchThat(alwaysTrue)
248250

@@ -1253,6 +1255,8 @@ object Denotations {
12531255
else sd1
12541256
else sd2
12551257
}
1258+
override def filterWithPredicate(p: SingleDenotation => Boolean): Denotation =
1259+
derivedUnionDenotation(denot1.filterWithPredicate(p), denot2.filterWithPredicate(p))
12561260
def hasAltWith(p: SingleDenotation => Boolean): Boolean =
12571261
denot1.hasAltWith(p) || denot2.hasAltWith(p)
12581262
def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = {

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,8 @@ object Flags {
592592
final val ImplicitOrImpliedOrGiven = Implicit | Implied | Given
593593
final val ImplicitOrGiven = Implicit | Given
594594

595+
final val ImpliedOrGiven = Implied | Given
596+
595597
final val ImplicitOrImpliedOrGivenTerm = ImplicitOrImpliedOrGiven.toTermFlags
596598

597599
/** Flags retained in export forwarders */

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Standard-Section: "ASTs" TopLevelStat*
6363
Modifier* -- modifiers def name [typeparams] paramss : returnType (= rhs)?
6464
Selector = IMPORTED name_NameRef -- name
6565
RENAMED to_NameRef -- => name
66+
BOUNDED type_Term? -- for type
6667
6768
TypeParam = TYPEPARAM Length NameRef type_Term Modifier* -- modifiers name bounds
6869
Params = PARAMS Length Param*
@@ -80,7 +81,7 @@ Standard-Section: "ASTs" TopLevelStat*
8081
APPLY Length fn_Term arg_Term* -- fn(args)
8182
TYPEAPPLY Length fn_Term arg_Type* -- fn[args]
8283
SUPER Length this_Term mixinTypeIdent_Tree? -- super[mixin]
83-
TYPED Length expr_Term ascriptionType_Tern -- expr: ascription
84+
TYPED Length expr_Term ascriptionType_Term -- expr: ascription
8485
ASSIGN Length lhs_Term rhs_Term -- lhs = rhs
8586
BLOCK Length expr_Term Stat* -- { stats; expr }
8687
INLINED Length expr_Term call_Term? ValOrDefDef* -- Inlined code from call, with given body `expr` and given bindings
@@ -369,6 +370,7 @@ object TastyFormat {
369370
final val RECtype = 91
370371
final val TYPEALIAS = 92
371372
final val SINGLETONtpt = 93
373+
final val BOUNDED = 94
372374

373375
// Cat. 4: tag Nat AST
374376

@@ -461,7 +463,7 @@ object TastyFormat {
461463
def isLegalTag(tag: Int): Boolean =
462464
firstSimpleTreeTag <= tag && tag <= EXPORTED ||
463465
firstNatTreeTag <= tag && tag <= SYMBOLconst ||
464-
firstASTTreeTag <= tag && tag <= SINGLETONtpt ||
466+
firstASTTreeTag <= tag && tag <= BOUNDED ||
465467
firstNatASTTreeTag <= tag && tag <= NAMEDARG ||
466468
firstLengthTreeTag <= tag && tag <= MATCHtpt ||
467469
tag == HOLE
@@ -601,6 +603,7 @@ object TastyFormat {
601603
case PARAM => "PARAM"
602604
case IMPORTED => "IMPORTED"
603605
case RENAMED => "RENAMED"
606+
case BOUNDED => "BOUNDED"
604607
case APPLY => "APPLY"
605608
case TYPEAPPLY => "TYPEAPPLY"
606609
case NEW => "NEW"

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,10 @@ class TreePickler(pickler: TastyPickler) {
606606
pickleSelector(RENAMED, to)
607607
case id @ Ident(_) =>
608608
pickleSelector(IMPORTED, id)
609+
case bounded @ TypeBoundsTree(untpd.EmptyTree, untpd.TypedSplice(tpt)) =>
610+
registerTreeAddr(bounded)
611+
writeByte(BOUNDED)
612+
pickleTree(tpt)
609613
}
610614

611615
def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,11 @@ class TreeUnpickler(reader: TastyReader,
973973
case _ =>
974974
from :: readSelectors()
975975
}
976+
case BOUNDED =>
977+
val start = currentAddr
978+
readByte()
979+
val bounded = setSpan(start, untpd.TypeBoundsTree(untpd.EmptyTree, untpd.TypedSplice(readTpt())))
980+
bounded :: readSelectors()
976981
case _ =>
977982
Nil
978983
}

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

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -627,9 +627,6 @@ object Parsers {
627627
def wildcardIdent(): Ident =
628628
atSpan(accept(USCORE)) { Ident(nme.WILDCARD) }
629629

630-
def termIdentOrWildcard(): Ident =
631-
if (in.token == USCORE) wildcardIdent() else termIdent()
632-
633630
/** Accept identifier acting as a selector on given tree `t`. */
634631
def selector(t: Tree): Tree =
635632
atSpan(startOffset(t), in.offset) { Select(t, ident()) }
@@ -2310,8 +2307,50 @@ object Parsers {
23102307
*/
23112308
def importExpr(importImplied: Boolean, mkTree: ImportConstr): () => Tree = {
23122309

2310+
/** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’
2311+
* FinalSelector ::= ImportSelector
2312+
* | ‘_’
2313+
* | ‘for’ InfixType
2314+
*/
2315+
def importSelectors(): List[Tree] = in.token match {
2316+
case USCORE =>
2317+
wildcardIdent() :: Nil
2318+
case FOR =>
2319+
if (!importImplied)
2320+
syntaxError(em"`for` qualifier only allowed in `import implied`")
2321+
atSpan(in.skipToken()) { TypeBoundsTree(EmptyTree, infixType()) } :: Nil
2322+
case _ =>
2323+
importSelector() :: {
2324+
if (in.token == COMMA) {
2325+
in.nextToken()
2326+
importSelectors()
2327+
}
2328+
else Nil
2329+
}
2330+
}
2331+
2332+
/** ImportSelector ::= id [`=>' id | `=>' `_']
2333+
*/
2334+
def importSelector(): Tree = {
2335+
val from = termIdent()
2336+
if (in.token == ARROW)
2337+
atSpan(startOffset(from), in.skipToken()) {
2338+
val start = in.offset
2339+
val to = if (in.token == USCORE) wildcardIdent() else termIdent()
2340+
val toWithPos =
2341+
if (to.name == nme.ERROR)
2342+
// error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span.
2343+
// Some testcases would then fail in Positioned.checkPos. Set a span anyway!
2344+
atSpan(start, start, in.lastOffset)(to)
2345+
else
2346+
to
2347+
Thicket(from, toWithPos)
2348+
}
2349+
else from
2350+
}
2351+
23132352
val handleImport: Tree => Tree = { tree: Tree =>
2314-
if (in.token == USCORE) mkTree(importImplied, tree, importSelector() :: Nil)
2353+
if (in.token == USCORE) mkTree(importImplied, tree, wildcardIdent() :: Nil)
23152354
else if (in.token == LBRACE) mkTree(importImplied, tree, inBraces(importSelectors()))
23162355
else tree
23172356
}
@@ -2333,41 +2372,6 @@ object Parsers {
23332372
}
23342373
}
23352374

2336-
/** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
2337-
*/
2338-
def importSelectors(): List[Tree] = {
2339-
val sel = importSelector()
2340-
if (in.token == RBRACE) sel :: Nil
2341-
else {
2342-
sel :: {
2343-
if (!isWildcardArg(sel) && in.token == COMMA) {
2344-
in.nextToken()
2345-
importSelectors()
2346-
}
2347-
else Nil
2348-
}
2349-
}
2350-
}
2351-
2352-
/** ImportSelector ::= id [`=>' id | `=>' `_']
2353-
*/
2354-
def importSelector(): Tree = {
2355-
val from = termIdentOrWildcard()
2356-
if (from.name != nme.WILDCARD && in.token == ARROW)
2357-
atSpan(startOffset(from), in.skipToken()) {
2358-
val start = in.offset
2359-
val to = termIdentOrWildcard()
2360-
val toWithPos =
2361-
if (to.name == nme.ERROR)
2362-
// error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span.
2363-
// Some testcases would then fail in Positioned.checkPos. Set a span anyway!
2364-
atSpan(start, start, in.lastOffset)(to)
2365-
else
2366-
to
2367-
Thicket(from, toWithPos)
2368-
}
2369-
else from
2370-
}
23712375

23722376
def posMods(start: Int, mods: Modifiers): Modifiers = {
23732377
in.nextToken()

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
501501
case Import(importImplied, expr, selectors) =>
502502
def selectorText(sel: Tree): Text = sel match {
503503
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
504-
case _ => toTextGlobal(sel)
504+
case _: Ident => toTextGlobal(sel)
505+
case TypeBoundsTree(_, tpt) => "for " ~ toTextGlobal(tpt)
505506
}
506507
val selectorsText: Text = selectors match {
507508
case id :: Nil => toText(id)

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

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import util.SimpleIdentityMap
1010
import Symbols._, Names._, Types._, Contexts._, StdNames._, Flags._
1111
import Implicits.RenamedImplicitRef
1212
import printing.Texts.Text
13+
import Decorators._
1314

1415
object ImportInfo {
1516
/** The import info for a root import from given symbol `sym` */
@@ -55,62 +56,86 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
5556
/** The names that are excluded from any wildcard import */
5657
def excluded: Set[TermName] = { ensureInitialized(); myExcluded }
5758

58-
/** A mapping from renamed to original names */
59-
def reverseMapping: SimpleIdentityMap[TermName, TermName] = { ensureInitialized(); myMapped }
59+
/** A mapping from original to renamed names */
60+
def forwardMapping: SimpleIdentityMap[TermName, TermName] = { ensureInitialized(); myForwardMapping }
6061

61-
/** The original names imported by-name before renaming */
62-
def originals: Set[TermName] = { ensureInitialized(); myOriginals }
62+
/** A mapping from renamed to original names */
63+
def reverseMapping: SimpleIdentityMap[TermName, TermName] = { ensureInitialized(); myReverseMapping }
6364

6465
/** Does the import clause end with wildcard? */
6566
def isWildcardImport: Boolean = { ensureInitialized(); myWildcardImport }
6667

6768
private[this] var myExcluded: Set[TermName] = null
68-
private[this] var myMapped: SimpleIdentityMap[TermName, TermName] = null
69-
private[this] var myOriginals: Set[TermName] = null
69+
private[this] var myForwardMapping: SimpleIdentityMap[TermName, TermName] = null
70+
private[this] var myReverseMapping: SimpleIdentityMap[TermName, TermName] = null
7071
private[this] var myWildcardImport: Boolean = false
7172

7273
/** Compute info relating to the selector list */
7374
private def ensureInitialized(): Unit = if (myExcluded == null) {
7475
myExcluded = Set()
75-
myMapped = SimpleIdentityMap.Empty
76-
myOriginals = Set()
76+
myForwardMapping = SimpleIdentityMap.Empty
77+
myReverseMapping = SimpleIdentityMap.Empty
7778
def recur(sels: List[untpd.Tree]): Unit = sels match {
7879
case sel :: sels1 =>
7980
sel match {
8081
case Thicket(Ident(name: TermName) :: Ident(nme.WILDCARD) :: Nil) =>
8182
myExcluded += name
8283
case Thicket(Ident(from: TermName) :: Ident(to: TermName) :: Nil) =>
83-
myMapped = myMapped.updated(to, from)
84+
myForwardMapping = myForwardMapping.updated(from, to)
85+
myReverseMapping = myReverseMapping.updated(to, from)
8486
myExcluded += from
85-
myOriginals += from
8687
case Ident(nme.WILDCARD) =>
8788
myWildcardImport = true
8889
case Ident(name: TermName) =>
89-
myMapped = myMapped.updated(name, name)
90-
myOriginals += name
90+
myForwardMapping = myForwardMapping.updated(name, name)
91+
myReverseMapping = myReverseMapping.updated(name, name)
92+
case TypeBoundsTree(_, tpt) =>
93+
myWildcardImport = true // details are handled separately in impliedBounds
9194
}
9295
recur(sels1)
9396
case nil =>
9497
}
9598
recur(selectors)
9699
}
97100

101+
private[this] var myImpliedBound: Type = null
102+
103+
def impliedBound(implicit ctx: Context): Type = {
104+
if (myImpliedBound == null)
105+
myImpliedBound = selectors.lastOption match {
106+
case Some(TypeBoundsTree(_, untpd.TypedSplice(tpt))) => tpt.tpe
107+
case Some(TypeBoundsTree(_, tpt)) =>
108+
myImpliedBound = NoType
109+
ctx.typer.typedAheadType(tpt).tpe
110+
case _ => NoType
111+
}
112+
myImpliedBound
113+
}
114+
98115
private def implicitFlag(implicit ctx: Context) =
99116
if (importImplied || ctx.mode.is(Mode.FindHiddenImplicits)) ImplicitOrImpliedOrGiven
100117
else Implicit
101118

102119
/** The implicit references imported by this import clause */
103120
def importedImplicits(implicit ctx: Context): List[ImplicitRef] = {
104121
val pre = site
105-
if (isWildcardImport) {
106-
val refs = pre.implicitMembers(implicitFlag)
107-
if (excluded.isEmpty) refs
108-
else refs filterNot (ref => excluded contains ref.name.toTermName)
109-
} else
110-
for {
122+
if (isWildcardImport)
123+
pre.implicitMembers(implicitFlag).flatMap { ref =>
124+
val name = ref.name.toTermName
125+
if (excluded.contains(name)) Nil
126+
else {
127+
val renamed = forwardMapping(ref.name)
128+
if (renamed == ref.name) ref :: Nil
129+
else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil
130+
else if (!impliedBound.exists || (ref <:< impliedBound)) ref :: Nil
131+
else Nil
132+
}
133+
}
134+
else
135+
for
111136
renamed <- reverseMapping.keys
112137
denot <- pre.member(reverseMapping(renamed)).altsWith(_ is implicitFlag)
113-
} yield {
138+
yield {
114139
val original = reverseMapping(renamed)
115140
val ref = TermRef(pre, original, denot)
116141
if (renamed == original) ref
@@ -149,7 +174,7 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
149174
def featureImported(feature: TermName, owner: Symbol)(implicit ctx: Context): Boolean = {
150175
def compute = {
151176
val isImportOwner = site.widen.typeSymbol.eq(owner)
152-
if (isImportOwner && originals.contains(feature)) true
177+
if (isImportOwner && forwardMapping.contains(feature)) true
153178
else if (isImportOwner && excluded.contains(feature)) false
154179
else {
155180
var c = ctx.outer

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class Typer extends Namer
177177
previous
178178
}
179179

180-
def selection(imp: ImportInfo, name: Name) =
180+
def selection(imp: ImportInfo, name: Name, checkBounds: Boolean) =
181181
if (imp.sym.isCompleting) {
182182
ctx.warning(i"cyclic ${imp.sym}, ignored", posd.sourcePos)
183183
NoType
@@ -188,7 +188,10 @@ class Typer extends Namer
188188
var reqd = required
189189
var excl = EmptyFlags
190190
if (imp.importImplied) reqd |= Implied else excl |= Implied
191-
val denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx)
191+
var denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx)
192+
if (checkBounds && imp.impliedBound.exists)
193+
denot = denot.filterWithPredicate(_.info <:< imp.impliedBound)
194+
192195
// Pass refctx so that any errors are reported in the context of the
193196
// reference instead of the
194197
if (reallyExists(denot)) pre.select(name, denot) else NoType
@@ -209,11 +212,10 @@ class Typer extends Namer
209212
}
210213

211214
def unambiguousSelection(name: Name) =
212-
checkUnambiguous(selection(imp, name))
215+
checkUnambiguous(selection(imp, name, checkBounds = false))
213216

214217
selector match {
215-
case Thicket(fromId :: Ident(Name) :: _) =>
216-
val Ident(from) = fromId
218+
case Thicket(Ident(from) :: Ident(Name) :: _) =>
217219
unambiguousSelection(if (name.isTypeName) from.toTypeName else from)
218220
case Ident(Name) =>
219221
unambiguousSelection(name)
@@ -231,7 +233,7 @@ class Typer extends Namer
231233
*/
232234
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type =
233235
if (imp.isWildcardImport && !imp.excluded.contains(name.toTermName) && name != nme.CONSTRUCTOR)
234-
selection(imp, name)
236+
selection(imp, name, checkBounds = imp.importImplied)
235237
else NoType
236238

237239
/** Is (some alternative of) the given predenotation `denot`
@@ -1785,7 +1787,12 @@ class Typer extends Namer
17851787
def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") {
17861788
val expr1 = typedExpr(imp.expr, AnySelectionProto)
17871789
checkLegalImportPath(expr1)
1788-
assignType(cpy.Import(imp)(imp.importImplied, expr1, imp.selectors), sym)
1790+
val selectors1: List[untpd.Tree] = imp.selectors map {
1791+
case sel @ TypeBoundsTree(_, tpt) =>
1792+
untpd.cpy.TypeBoundsTree(sel)(sel.lo, untpd.TypedSplice(typedType(tpt)))
1793+
case sel => sel
1794+
}
1795+
assignType(cpy.Import(imp)(imp.importImplied, expr1, selectors1), sym)
17891796
}
17901797

17911798
def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") {

0 commit comments

Comments
 (0)