Skip to content

Implement for clauses for implied imports #6594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ object Denotations {
*/
def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation

override def filterWithPredicate(p: SingleDenotation => Boolean): Denotation

/** If this is a SingleDenotation, return it, otherwise throw a TypeError */
def checkUnique(implicit ctx: Context): SingleDenotation = suchThat(alwaysTrue)

Expand Down Expand Up @@ -1253,6 +1255,8 @@ object Denotations {
else sd1
else sd2
}
override def filterWithPredicate(p: SingleDenotation => Boolean): Denotation =
derivedUnionDenotation(denot1.filterWithPredicate(p), denot2.filterWithPredicate(p))
def hasAltWith(p: SingleDenotation => Boolean): Boolean =
denot1.hasAltWith(p) || denot2.hasAltWith(p)
def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = {
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,8 @@ object Flags {
final val ImplicitOrImpliedOrGiven = Implicit | Implied | Given
final val ImplicitOrGiven = Implicit | Given

final val ImpliedOrGiven = Implied | Given

final val ImplicitOrImpliedOrGivenTerm = ImplicitOrImpliedOrGiven.toTermFlags

/** Flags retained in export forwarders */
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Standard-Section: "ASTs" TopLevelStat*
Modifier* -- modifiers def name [typeparams] paramss : returnType (= rhs)?
Selector = IMPORTED name_NameRef -- name
RENAMED to_NameRef -- => name
BOUNDED type_Term? -- for type

TypeParam = TYPEPARAM Length NameRef type_Term Modifier* -- modifiers name bounds
Params = PARAMS Length Param*
Expand All @@ -80,7 +81,7 @@ Standard-Section: "ASTs" TopLevelStat*
APPLY Length fn_Term arg_Term* -- fn(args)
TYPEAPPLY Length fn_Term arg_Type* -- fn[args]
SUPER Length this_Term mixinTypeIdent_Tree? -- super[mixin]
TYPED Length expr_Term ascriptionType_Tern -- expr: ascription
TYPED Length expr_Term ascriptionType_Term -- expr: ascription
ASSIGN Length lhs_Term rhs_Term -- lhs = rhs
BLOCK Length expr_Term Stat* -- { stats; expr }
INLINED Length expr_Term call_Term? ValOrDefDef* -- Inlined code from call, with given body `expr` and given bindings
Expand Down Expand Up @@ -369,6 +370,7 @@ object TastyFormat {
final val RECtype = 91
final val TYPEALIAS = 92
final val SINGLETONtpt = 93
final val BOUNDED = 94

// Cat. 4: tag Nat AST

Expand Down Expand Up @@ -461,7 +463,7 @@ object TastyFormat {
def isLegalTag(tag: Int): Boolean =
firstSimpleTreeTag <= tag && tag <= EXPORTED ||
firstNatTreeTag <= tag && tag <= SYMBOLconst ||
firstASTTreeTag <= tag && tag <= SINGLETONtpt ||
firstASTTreeTag <= tag && tag <= BOUNDED ||
firstNatASTTreeTag <= tag && tag <= NAMEDARG ||
firstLengthTreeTag <= tag && tag <= MATCHtpt ||
tag == HOLE
Expand Down Expand Up @@ -601,6 +603,7 @@ object TastyFormat {
case PARAM => "PARAM"
case IMPORTED => "IMPORTED"
case RENAMED => "RENAMED"
case BOUNDED => "BOUNDED"
case APPLY => "APPLY"
case TYPEAPPLY => "TYPEAPPLY"
case NEW => "NEW"
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,10 @@ class TreePickler(pickler: TastyPickler) {
pickleSelector(RENAMED, to)
case id @ Ident(_) =>
pickleSelector(IMPORTED, id)
case bounded @ TypeBoundsTree(untpd.EmptyTree, untpd.TypedSplice(tpt)) =>
registerTreeAddr(bounded)
writeByte(BOUNDED)
pickleTree(tpt)
}

def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = {
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,11 @@ class TreeUnpickler(reader: TastyReader,
case _ =>
from :: readSelectors()
}
case BOUNDED =>
val start = currentAddr
readByte()
val bounded = setSpan(start, untpd.TypeBoundsTree(untpd.EmptyTree, untpd.TypedSplice(readTpt())))
bounded :: readSelectors()
case _ =>
Nil
}
Expand Down
82 changes: 43 additions & 39 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -627,9 +627,6 @@ object Parsers {
def wildcardIdent(): Ident =
atSpan(accept(USCORE)) { Ident(nme.WILDCARD) }

def termIdentOrWildcard(): Ident =
if (in.token == USCORE) wildcardIdent() else termIdent()

/** Accept identifier acting as a selector on given tree `t`. */
def selector(t: Tree): Tree =
atSpan(startOffset(t), in.offset) { Select(t, ident()) }
Expand Down Expand Up @@ -2310,8 +2307,50 @@ object Parsers {
*/
def importExpr(importImplied: Boolean, mkTree: ImportConstr): () => Tree = {

/** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’
* FinalSelector ::= ImportSelector
* | ‘_’
* | ‘for’ InfixType
*/
def importSelectors(): List[Tree] = in.token match {
case USCORE =>
wildcardIdent() :: Nil
case FOR =>
if (!importImplied)
syntaxError(em"`for` qualifier only allowed in `import implied`")
atSpan(in.skipToken()) { TypeBoundsTree(EmptyTree, infixType()) } :: Nil
case _ =>
importSelector() :: {
if (in.token == COMMA) {
in.nextToken()
importSelectors()
}
else Nil
}
}

/** ImportSelector ::= id [`=>' id | `=>' `_']
*/
def importSelector(): Tree = {
val from = termIdent()
if (in.token == ARROW)
atSpan(startOffset(from), in.skipToken()) {
val start = in.offset
val to = if (in.token == USCORE) wildcardIdent() else termIdent()
val toWithPos =
if (to.name == nme.ERROR)
// error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span.
// Some testcases would then fail in Positioned.checkPos. Set a span anyway!
atSpan(start, start, in.lastOffset)(to)
else
to
Thicket(from, toWithPos)
}
else from
}

val handleImport: Tree => Tree = { tree: Tree =>
if (in.token == USCORE) mkTree(importImplied, tree, importSelector() :: Nil)
if (in.token == USCORE) mkTree(importImplied, tree, wildcardIdent() :: Nil)
else if (in.token == LBRACE) mkTree(importImplied, tree, inBraces(importSelectors()))
else tree
}
Expand All @@ -2333,41 +2372,6 @@ object Parsers {
}
}

/** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
*/
def importSelectors(): List[Tree] = {
val sel = importSelector()
if (in.token == RBRACE) sel :: Nil
else {
sel :: {
if (!isWildcardArg(sel) && in.token == COMMA) {
in.nextToken()
importSelectors()
}
else Nil
}
}
}

/** ImportSelector ::= id [`=>' id | `=>' `_']
*/
def importSelector(): Tree = {
val from = termIdentOrWildcard()
if (from.name != nme.WILDCARD && in.token == ARROW)
atSpan(startOffset(from), in.skipToken()) {
val start = in.offset
val to = termIdentOrWildcard()
val toWithPos =
if (to.name == nme.ERROR)
// error identifiers don't consume any characters, so atSpan(start)(id) wouldn't set a span.
// Some testcases would then fail in Positioned.checkPos. Set a span anyway!
atSpan(start, start, in.lastOffset)(to)
else
to
Thicket(from, toWithPos)
}
else from
}

def posMods(start: Int, mods: Modifiers): Modifiers = {
in.nextToken()
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case Import(importImplied, expr, selectors) =>
def selectorText(sel: Tree): Text = sel match {
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
case _ => toTextGlobal(sel)
case _: Ident => toTextGlobal(sel)
case TypeBoundsTree(_, tpt) => "for " ~ toTextGlobal(tpt)
}
val selectorsText: Text = selectors match {
case id :: Nil => toText(id)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1353,7 +1353,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
}
def prepare(tp: Type) = tp.stripTypeVar match {
case tp: NamedType if tp.symbol.is(Module) && tp.symbol.sourceModule.is(Implied) =>
flip(tp.widen.widenToParents
flip(tp.widen.widenToParents)
case _ => flip(tp)
}
(prepare(tp1) relaxed_<:< prepare(tp2)) || viewExists(tp1, tp2)
Expand Down
65 changes: 45 additions & 20 deletions compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import util.SimpleIdentityMap
import Symbols._, Names._, Types._, Contexts._, StdNames._, Flags._
import Implicits.RenamedImplicitRef
import printing.Texts.Text
import Decorators._

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

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

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

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

private[this] var myExcluded: Set[TermName] = null
private[this] var myMapped: SimpleIdentityMap[TermName, TermName] = null
private[this] var myOriginals: Set[TermName] = null
private[this] var myForwardMapping: SimpleIdentityMap[TermName, TermName] = null
private[this] var myReverseMapping: SimpleIdentityMap[TermName, TermName] = null
private[this] var myWildcardImport: Boolean = false

/** Compute info relating to the selector list */
private def ensureInitialized(): Unit = if (myExcluded == null) {
myExcluded = Set()
myMapped = SimpleIdentityMap.Empty
myOriginals = Set()
myForwardMapping = SimpleIdentityMap.Empty
myReverseMapping = SimpleIdentityMap.Empty
def recur(sels: List[untpd.Tree]): Unit = sels match {
case sel :: sels1 =>
sel match {
case Thicket(Ident(name: TermName) :: Ident(nme.WILDCARD) :: Nil) =>
myExcluded += name
case Thicket(Ident(from: TermName) :: Ident(to: TermName) :: Nil) =>
myMapped = myMapped.updated(to, from)
myForwardMapping = myForwardMapping.updated(from, to)
myReverseMapping = myReverseMapping.updated(to, from)
myExcluded += from
myOriginals += from
case Ident(nme.WILDCARD) =>
myWildcardImport = true
case Ident(name: TermName) =>
myMapped = myMapped.updated(name, name)
myOriginals += name
myForwardMapping = myForwardMapping.updated(name, name)
myReverseMapping = myReverseMapping.updated(name, name)
case TypeBoundsTree(_, tpt) =>
myWildcardImport = true // details are handled separately in impliedBounds
}
recur(sels1)
case nil =>
}
recur(selectors)
}

private[this] var myImpliedBound: Type = null

def impliedBound(implicit ctx: Context): Type = {
if (myImpliedBound == null)
myImpliedBound = selectors.lastOption match {
case Some(TypeBoundsTree(_, untpd.TypedSplice(tpt))) => tpt.tpe
case Some(TypeBoundsTree(_, tpt)) =>
myImpliedBound = NoType
ctx.typer.typedAheadType(tpt).tpe
case _ => NoType
}
myImpliedBound
}

private def implicitFlag(implicit ctx: Context) =
if (importImplied || ctx.mode.is(Mode.FindHiddenImplicits)) ImplicitOrImpliedOrGiven
else Implicit

/** The implicit references imported by this import clause */
def importedImplicits(implicit ctx: Context): List[ImplicitRef] = {
val pre = site
if (isWildcardImport) {
val refs = pre.implicitMembers(implicitFlag)
if (excluded.isEmpty) refs
else refs filterNot (ref => excluded contains ref.name.toTermName)
} else
for {
if (isWildcardImport)
pre.implicitMembers(implicitFlag).flatMap { ref =>
val name = ref.name.toTermName
if (excluded.contains(name)) Nil
else {
val renamed = forwardMapping(ref.name)
if (renamed == ref.name) ref :: Nil
else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil
else if (!impliedBound.exists || (ref <:< impliedBound)) ref :: Nil
else Nil
}
}
else
for
renamed <- reverseMapping.keys
denot <- pre.member(reverseMapping(renamed)).altsWith(_ is implicitFlag)
} yield {
yield {
val original = reverseMapping(renamed)
val ref = TermRef(pre, original, denot)
if (renamed == original) ref
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice for syntax 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum ... It doesn't look like valid Scala code to me, actually :-s Maybe with the indentation-based syntax, but that shouldn't be enabled there? Is the parser too lenient?

Expand Down Expand Up @@ -149,7 +174,7 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
def featureImported(feature: TermName, owner: Symbol)(implicit ctx: Context): Boolean = {
def compute = {
val isImportOwner = site.widen.typeSymbol.eq(owner)
if (isImportOwner && originals.contains(feature)) true
if (isImportOwner && forwardMapping.contains(feature)) true
else if (isImportOwner && excluded.contains(feature)) false
else {
var c = ctx.outer
Expand Down
21 changes: 14 additions & 7 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ class Typer extends Namer
previous
}

def selection(imp: ImportInfo, name: Name) =
def selection(imp: ImportInfo, name: Name, checkBounds: Boolean) =
if (imp.sym.isCompleting) {
ctx.warning(i"cyclic ${imp.sym}, ignored", posd.sourcePos)
NoType
Expand All @@ -188,7 +188,10 @@ class Typer extends Namer
var reqd = required
var excl = EmptyFlags
if (imp.importImplied) reqd |= Implied else excl |= Implied
val denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx)
var denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx)
if (checkBounds && imp.impliedBound.exists)
denot = denot.filterWithPredicate(_.info <:< imp.impliedBound)

// Pass refctx so that any errors are reported in the context of the
// reference instead of the
if (reallyExists(denot)) pre.select(name, denot) else NoType
Expand All @@ -209,11 +212,10 @@ class Typer extends Namer
}

def unambiguousSelection(name: Name) =
checkUnambiguous(selection(imp, name))
checkUnambiguous(selection(imp, name, checkBounds = false))

selector match {
case Thicket(fromId :: Ident(Name) :: _) =>
val Ident(from) = fromId
case Thicket(Ident(from) :: Ident(Name) :: _) =>
unambiguousSelection(if (name.isTypeName) from.toTypeName else from)
case Ident(Name) =>
unambiguousSelection(name)
Expand All @@ -231,7 +233,7 @@ class Typer extends Namer
*/
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type =
if (imp.isWildcardImport && !imp.excluded.contains(name.toTermName) && name != nme.CONSTRUCTOR)
selection(imp, name)
selection(imp, name, checkBounds = imp.importImplied)
else NoType

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

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