Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/TypeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ object TypeUtils {
case _ => if (ctx.erasedTypes) MethodType(Nil, self) else ExprType(self)
}

def widenToParents(implicit ctx: Context): Type = self.parents match {
case Nil => self
case ps => ps.reduceLeft(AndType(_, _))
}

/** The arity of this tuple type, which can be made up of Unit, TupleX and `*:` pairs,
* or -1 if this is not a tuple type.
*/
Expand Down
17 changes: 11 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import StdNames._
import NameKinds.DefaultGetterName
import ProtoTypes._
import Inferencing._
import transform.TypeUtils._

import collection.mutable
import config.Printers.{overload, typr, unapp}
Expand Down Expand Up @@ -1332,6 +1333,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
* iff
*
* T => R <:s U => R
*
* Also: If a compared type refers to an implied object or its module class, use
* the intersection of its parent classes instead.
*/
def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) =
if (ctx.mode.is(Mode.OldOverloadingResolution))
Expand All @@ -1347,7 +1351,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
case _ => mapOver(t)
}
}
(flip(tp1) relaxed_<:< flip(tp2)) || viewExists(tp1, tp2)
def prepare(tp: Type) = tp.stripTypeVar match {
case tp: NamedType if tp.symbol.is(Module) && tp.symbol.sourceModule.is(Implied) =>
flip(tp.widen.widenToParents)
case _ => flip(tp)
}
(prepare(tp1) relaxed_<:< prepare(tp2)) || viewExists(tp1, tp2)
}

/** Widen the result type of synthetic implied methods from the implementation class to the
Expand Down Expand Up @@ -1375,11 +1384,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
case pt: PolyType =>
pt.derivedLambdaType(pt.paramNames, pt.paramInfos, widenImplied(pt.resultType, alt))
case _ =>
if (alt.symbol.is(SyntheticImpliedMethod))
tp.parents match {
case Nil => tp
case ps => ps.reduceLeft(AndType(_, _))
}
if (alt.symbol.is(SyntheticImpliedMethod)) tp.widenToParents
else tp
}

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
Loading