Skip to content

Commit cc6cb05

Browse files
authored
Merge pull request #6594 from dotty-staging/change-implied-imports
Implement `for` clauses for implied imports
2 parents 3736816 + 498f081 commit cc6cb05

File tree

15 files changed

+260
-77
lines changed

15 files changed

+260
-77
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: 50 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -640,9 +640,6 @@ object Parsers {
640640
def wildcardIdent(): Ident =
641641
atSpan(accept(USCORE)) { Ident(nme.WILDCARD) }
642642

643-
def termIdentOrWildcard(): Ident =
644-
if (in.token == USCORE) wildcardIdent() else termIdent()
645-
646643
/** Accept identifier acting as a selector on given tree `t`. */
647644
def selector(t: Tree): Tree =
648645
atSpan(startOffset(t), in.offset) { Select(t, ident()) }
@@ -2352,8 +2349,57 @@ object Parsers {
23522349
*/
23532350
def importExpr(importImplied: Boolean, mkTree: ImportConstr): () => Tree = {
23542351

2352+
/** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’
2353+
* FinalSelector ::= ImportSelector
2354+
* | ‘_’
2355+
* | ‘for’ InfixType {‘,’ InfixType}
2356+
*/
2357+
def importSelectors(): List[Tree] = in.token match {
2358+
case USCORE =>
2359+
wildcardIdent() :: Nil
2360+
case FOR =>
2361+
if (!importImplied)
2362+
syntaxError(em"`for` qualifier only allowed in `import implied`")
2363+
atSpan(in.skipToken()) {
2364+
var t = infixType()
2365+
while (in.token == COMMA) {
2366+
val op = atSpan(in.skipToken()) { Ident(tpnme.raw.BAR) }
2367+
t = InfixOp(t, op, infixType())
2368+
}
2369+
TypeBoundsTree(EmptyTree, t)
2370+
} :: Nil
2371+
case _ =>
2372+
importSelector() :: {
2373+
if (in.token == COMMA) {
2374+
in.nextToken()
2375+
importSelectors()
2376+
}
2377+
else Nil
2378+
}
2379+
}
2380+
2381+
/** ImportSelector ::= id [`=>' id | `=>' `_']
2382+
*/
2383+
def importSelector(): Tree = {
2384+
val from = termIdent()
2385+
if (in.token == ARROW)
2386+
atSpan(startOffset(from), in.skipToken()) {
2387+
val start = in.offset
2388+
val to = if (in.token == USCORE) wildcardIdent() else termIdent()
2389+
val toWithPos =
2390+
if (to.name == nme.ERROR)
2391+
// error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span.
2392+
// Some testcases would then fail in Positioned.checkPos. Set a span anyway!
2393+
atSpan(start, start, in.lastOffset)(to)
2394+
else
2395+
to
2396+
Thicket(from, toWithPos)
2397+
}
2398+
else from
2399+
}
2400+
23552401
val handleImport: Tree => Tree = { tree: Tree =>
2356-
if (in.token == USCORE) mkTree(importImplied, tree, importSelector() :: Nil)
2402+
if (in.token == USCORE) mkTree(importImplied, tree, wildcardIdent() :: Nil)
23572403
else if (in.token == LBRACE) mkTree(importImplied, tree, inBraces(importSelectors()))
23582404
else tree
23592405
}
@@ -2375,41 +2421,6 @@ object Parsers {
23752421
}
23762422
}
23772423

2378-
/** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
2379-
*/
2380-
def importSelectors(): List[Tree] = {
2381-
val sel = importSelector()
2382-
if (in.token == RBRACE) sel :: Nil
2383-
else {
2384-
sel :: {
2385-
if (!isWildcardArg(sel) && in.token == COMMA) {
2386-
in.nextToken()
2387-
importSelectors()
2388-
}
2389-
else Nil
2390-
}
2391-
}
2392-
}
2393-
2394-
/** ImportSelector ::= id [`=>' id | `=>' `_']
2395-
*/
2396-
def importSelector(): Tree = {
2397-
val from = termIdentOrWildcard()
2398-
if (from.name != nme.WILDCARD && in.token == ARROW)
2399-
atSpan(startOffset(from), in.skipToken()) {
2400-
val start = in.offset
2401-
val to = termIdentOrWildcard()
2402-
val toWithPos =
2403-
if (to.name == nme.ERROR)
2404-
// error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span.
2405-
// Some testcases would then fail in Positioned.checkPos. Set a span anyway!
2406-
atSpan(start, start, in.lastOffset)(to)
2407-
else
2408-
to
2409-
Thicket(from, toWithPos)
2410-
}
2411-
else from
2412-
}
24132424

24142425
def posMods(start: Int, mods: Modifiers): Modifiers = {
24152426
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/transform/TypeUtils.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ object TypeUtils {
2525
case _ => if (ctx.erasedTypes) MethodType(Nil, self) else ExprType(self)
2626
}
2727

28+
def widenToParents(implicit ctx: Context): Type = self.parents match {
29+
case Nil => self
30+
case ps => ps.reduceLeft(AndType(_, _))
31+
}
32+
2833
/** The arity of this tuple type, which can be made up of Unit, TupleX and `*:` pairs,
2934
* or -1 if this is not a tuple type.
3035
*/

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import StdNames._
2121
import NameKinds.DefaultGetterName
2222
import ProtoTypes._
2323
import Inferencing._
24+
import transform.TypeUtils._
2425

2526
import collection.mutable
2627
import config.Printers.{overload, typr, unapp}
@@ -1332,6 +1333,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13321333
* iff
13331334
*
13341335
* T => R <:s U => R
1336+
*
1337+
* Also: If a compared type refers to an implied object or its module class, use
1338+
* the intersection of its parent classes instead.
13351339
*/
13361340
def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) =
13371341
if (ctx.mode.is(Mode.OldOverloadingResolution))
@@ -1347,7 +1351,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13471351
case _ => mapOver(t)
13481352
}
13491353
}
1350-
(flip(tp1) relaxed_<:< flip(tp2)) || viewExists(tp1, tp2)
1354+
def prepare(tp: Type) = tp.stripTypeVar match {
1355+
case tp: NamedType if tp.symbol.is(Module) && tp.symbol.sourceModule.is(Implied) =>
1356+
flip(tp.widen.widenToParents)
1357+
case _ => flip(tp)
1358+
}
1359+
(prepare(tp1) relaxed_<:< prepare(tp2)) || viewExists(tp1, tp2)
13511360
}
13521361

13531362
/** Widen the result type of synthetic implied methods from the implementation class to the
@@ -1375,11 +1384,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13751384
case pt: PolyType =>
13761385
pt.derivedLambdaType(pt.paramNames, pt.paramInfos, widenImplied(pt.resultType, alt))
13771386
case _ =>
1378-
if (alt.symbol.is(SyntheticImpliedMethod))
1379-
tp.parents match {
1380-
case Nil => tp
1381-
case ps => ps.reduceLeft(AndType(_, _))
1382-
}
1387+
if (alt.symbol.is(SyntheticImpliedMethod)) tp.widenToParents
13831388
else tp
13841389
}
13851390

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

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import util.SimpleIdentityMap
1010
import Symbols._, Names._, Types._, Contexts._, StdNames._, Flags._
1111
import Implicits.RenamedImplicitRef
1212
import printing.Texts.Text
13+
import ProtoTypes.NoViewsAllowed.normalizedCompatible
14+
import Decorators._
1315

1416
object ImportInfo {
1517
/** The import info for a root import from given symbol `sym` */
@@ -55,62 +57,87 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
5557
/** The names that are excluded from any wildcard import */
5658
def excluded: Set[TermName] = { ensureInitialized(); myExcluded }
5759

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

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

6466
/** Does the import clause end with wildcard? */
6567
def isWildcardImport: Boolean = { ensureInitialized(); myWildcardImport }
6668

6769
private[this] var myExcluded: Set[TermName] = null
68-
private[this] var myMapped: SimpleIdentityMap[TermName, TermName] = null
69-
private[this] var myOriginals: Set[TermName] = null
70+
private[this] var myForwardMapping: SimpleIdentityMap[TermName, TermName] = null
71+
private[this] var myReverseMapping: SimpleIdentityMap[TermName, TermName] = null
7072
private[this] var myWildcardImport: Boolean = false
7173

7274
/** Compute info relating to the selector list */
7375
private def ensureInitialized(): Unit = if (myExcluded == null) {
7476
myExcluded = Set()
75-
myMapped = SimpleIdentityMap.Empty
76-
myOriginals = Set()
77+
myForwardMapping = SimpleIdentityMap.Empty
78+
myReverseMapping = SimpleIdentityMap.Empty
7779
def recur(sels: List[untpd.Tree]): Unit = sels match {
7880
case sel :: sels1 =>
7981
sel match {
8082
case Thicket(Ident(name: TermName) :: Ident(nme.WILDCARD) :: Nil) =>
8183
myExcluded += name
8284
case Thicket(Ident(from: TermName) :: Ident(to: TermName) :: Nil) =>
83-
myMapped = myMapped.updated(to, from)
85+
myForwardMapping = myForwardMapping.updated(from, to)
86+
myReverseMapping = myReverseMapping.updated(to, from)
8487
myExcluded += from
85-
myOriginals += from
8688
case Ident(nme.WILDCARD) =>
8789
myWildcardImport = true
8890
case Ident(name: TermName) =>
89-
myMapped = myMapped.updated(name, name)
90-
myOriginals += name
91+
myForwardMapping = myForwardMapping.updated(name, name)
92+
myReverseMapping = myReverseMapping.updated(name, name)
93+
case TypeBoundsTree(_, tpt) =>
94+
myWildcardImport = true // details are handled separately in impliedBounds
9195
}
9296
recur(sels1)
9397
case nil =>
9498
}
9599
recur(selectors)
96100
}
97101

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

102120
/** The implicit references imported by this import clause */
103121
def importedImplicits(implicit ctx: Context): List[ImplicitRef] = {
104122
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 {
123+
if (isWildcardImport)
124+
pre.implicitMembers(implicitFlag).flatMap { ref =>
125+
val name = ref.name.toTermName
126+
if (excluded.contains(name)) Nil
127+
else {
128+
val renamed = forwardMapping(ref.name)
129+
if (renamed == ref.name) ref :: Nil
130+
else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil
131+
else if (!impliedBound.exists ||
132+
normalizedCompatible(ref, impliedBound, keepConstraint = false)) ref :: Nil
133+
else Nil
134+
}
135+
}
136+
else
137+
for
111138
renamed <- reverseMapping.keys
112139
denot <- pre.member(reverseMapping(renamed)).altsWith(_ is implicitFlag)
113-
} yield {
140+
yield {
114141
val original = reverseMapping(renamed)
115142
val ref = TermRef(pre, original, denot)
116143
if (renamed == original) ref
@@ -149,7 +176,7 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
149176
def featureImported(feature: TermName, owner: Symbol)(implicit ctx: Context): Boolean = {
150177
def compute = {
151178
val isImportOwner = site.widen.typeSymbol.eq(owner)
152-
if (isImportOwner && originals.contains(feature)) true
179+
if (isImportOwner && forwardMapping.contains(feature)) true
153180
else if (isImportOwner && excluded.contains(feature)) false
154181
else {
155182
var c = ctx.outer

0 commit comments

Comments
 (0)