Skip to content

Allow new import syntax #11244

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 10 commits into from
Feb 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion bench/tests/Vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ package immutable

import scala.annotation.unchecked.uncheckedVariance
import scala.compat.Platform
import scala.collection.generic._
import scala.collection.generic.*
import scala.collection.mutable.Builder
import compiletime.uninitialized

Expand Down
2 changes: 1 addition & 1 deletion bench/tests/power-macro/PowerInlined-1.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
object PowerInlined {
import PowerMacro._
import PowerMacro.*

power(1, 5.0) // 1 quotes to unpickle
}
2 changes: 1 addition & 1 deletion bench/tests/power-macro/PowerInlined.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
object PowerInlined {
import PowerMacro._
import PowerMacro.*

power(1, 5.0) // 1 quotes to unpickle
}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => false
}

/** Is this argument node of the form <expr> : _*, or is it a reference to
/** Is this argument node of the form <expr> *, or is it a reference to
* such an argument ? The latter case can happen when an argument is lifted.
*/
def isWildcardStarArg(tree: Tree)(using Context): Boolean = unbind(tree) match {
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,7 @@ object Trees {
type Template = Trees.Template[T]
type Import = Trees.Import[T]
type Export = Trees.Export[T]
type ImportOrExport = Trees.ImportOrExport[T]
type PackageDef = Trees.PackageDef[T]
type Annotated = Trees.Annotated[T]
type Thicket = Trees.Thicket[T]
Expand Down
142 changes: 83 additions & 59 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3066,8 +3066,8 @@ object Parsers {

type ImportConstr = (Tree, List[ImportSelector]) => Tree

/** Import ::= `import' [`given'] [ImportExpr {`,' ImportExpr}
* Export ::= `export' [`given'] [ImportExpr {`,' ImportExpr}
/** Import ::= `import' ImportExpr {‘,’ ImportExpr}
* Export ::= `export' ImportExpr {‘,’ ImportExpr}
*/
def importClause(leading: Token, mkTree: ImportConstr): List[Tree] = {
val offset = accept(leading)
Expand Down Expand Up @@ -3097,48 +3097,62 @@ object Parsers {
ctx.compilationUnit.sourceVersion = Some(SourceVersion.valueOf(imported.toString))
Import(tree, selectors)

/** ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec
* ImportSpec ::= id
* | ‘_’
* | ‘given’
* | ‘{’ ImportSelectors) ‘}’
*/
def importExpr(mkTree: ImportConstr): () => Tree = {

/** '_' */
def wildcardSelectorId() = atSpan(in.skipToken()) { Ident(nme.WILDCARD) }
def givenSelectorId(start: Offset) = atSpan(start) { Ident(nme.EMPTY) }
/** ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec
* | SimpleRef ‘as’ id
* ImportSpec ::= NamedSelector
* | WildcardSelector
* | ‘{’ ImportSelectors ‘}’
* ImportSelectors ::= NamedSelector [‘,’ ImportSelectors]
* | WildCardSelector {‘,’ WildCardSelector}
* NamedSelector ::= id [‘as’ (id | ‘_’)]
* WildCardSelector ::= ‘*' | ‘given’ [InfixType]
*/
def importExpr(mkTree: ImportConstr): () => Tree =

/** ‘*' | ‘_' */
def wildcardSelector() =
if in.token == USCORE && sourceVersion.isAtLeast(`3.1`) then
report.errorOrMigrationWarning(
em"`_` is no longer supported for a wildcard import; use `*` instead${rewriteNotice("3.1")}",
in.sourcePos())
patch(source, Span(in.offset, in.offset + 1), "*")
ImportSelector(atSpan(in.skipToken()) { Ident(nme.WILDCARD) })

/** 'given [InfixType]' */
def givenSelector() =
ImportSelector(
atSpan(in.skipToken()) { Ident(nme.EMPTY) },
bound =
if canStartTypeTokens.contains(in.token) then rejectWildcardType(infixType())
else EmptyTree)

/** id [‘as’ (id | ‘_’) */
def namedSelector(from: Ident) =
if in.token == ARROW || isIdent(nme.as) then
if in.token == ARROW && sourceVersion.isAtLeast(`3.1`) then
report.errorOrMigrationWarning(
em"The import renaming `a => b` is no longer supported ; use `a as b` instead${rewriteNotice("3.1")}",
in.sourcePos())
patch(source, Span(in.offset, in.offset + 2),
if testChar(in.offset - 1, ' ') && testChar(in.offset + 2, ' ') then "as"
else " as ")
atSpan(startOffset(from), in.skipToken()) {
val to = if in.token == USCORE then wildcardIdent() else termIdent()
ImportSelector(from, if to.name == nme.ERROR then EmptyTree else to)
}
else ImportSelector(from)

/** ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
* | WildCardSelector {‘,’ WildCardSelector}
* WildCardSelector ::= ‘given’ [InfixType]
* | ‘_'
*/
def importSelectors(idOK: Boolean): List[ImportSelector] =
val isWildcard = in.token == USCORE || in.token == GIVEN
val isWildcard = in.token == USCORE || in.token == GIVEN || isIdent(nme.raw.STAR)
val selector = atSpan(in.offset) {
in.token match
case USCORE =>
ImportSelector(wildcardSelectorId())
case GIVEN =>
val start = in.skipToken()
if in.token == USCORE then
deprecationWarning(em"`given _` is deprecated in imports; replace with just `given`", start)
in.nextToken()
ImportSelector(givenSelectorId(start)) // Let the selector span all of `given`; needed for -Ytest-pickler
else if canStartTypeTokens.contains(in.token) then
ImportSelector(givenSelectorId(start), bound = rejectWildcardType(infixType()))
else
ImportSelector(givenSelectorId(start))
case USCORE => wildcardSelector()
case GIVEN => givenSelector()
case _ =>
val from = termIdent()
if !idOK then syntaxError(i"named imports cannot follow wildcard imports")
if in.token == ARROW then
atSpan(startOffset(from), in.skipToken()) {
val to = if in.token == USCORE then wildcardIdent() else termIdent()
ImportSelector(from, if to.name == nme.ERROR then EmptyTree else to)
}
else ImportSelector(from)
if isIdent(nme.raw.STAR) then wildcardSelector()
else
if !idOK then syntaxError(i"named imports cannot follow wildcard imports")
namedSelector(termIdent())
}
val rest =
if in.token == COMMA then
Expand All @@ -3149,26 +3163,36 @@ object Parsers {
selector :: rest

def importSelection(qual: Tree): Tree =
accept(DOT)
in.token match
case USCORE =>
mkTree(qual, ImportSelector(wildcardSelectorId()) :: Nil)
case GIVEN =>
mkTree(qual, ImportSelector(givenSelectorId(in.skipToken())) :: Nil)
case LBRACE =>
mkTree(qual, inBraces(importSelectors(idOK = true)))
case _ =>
val start = in.offset
val name = ident()
if in.token == DOT then
importSelection(atSpan(startOffset(qual), start) { Select(qual, name) })
else
atSpan(startOffset(qual)) {
mkTree(qual, ImportSelector(atSpan(start) { Ident(name) }) :: Nil)
}

() => importSelection(simpleRef())
}
if in.isIdent(nme.as) && qual.isInstanceOf[RefTree] then
qual match
case Select(qual1, name) =>
val from = Ident(name).withSpan(Span(qual.span.point, qual.span.end, 0))
mkTree(qual1, namedSelector(from) :: Nil)
case qual: Ident =>
mkTree(EmptyTree, namedSelector(qual) :: Nil)
else
accept(DOT)
in.token match
case USCORE =>
mkTree(qual, wildcardSelector() :: Nil)
case GIVEN =>
mkTree(qual, givenSelector() :: Nil)
case LBRACE =>
mkTree(qual, inBraces(importSelectors(idOK = true)))
case _ =>
if isIdent(nme.raw.STAR) then
mkTree(qual, wildcardSelector() :: Nil)
else
val start = in.offset
val name = ident()
if in.token == DOT then
importSelection(atSpan(startOffset(qual), start) { Select(qual, name) })
else
mkTree(qual, namedSelector(atSpan(start) { Ident(name) }) :: Nil)
end importSelection

() => atSpan(in.offset) { importSelection(simpleRef()) }
end importExpr

/** Def ::= val PatDef
* | var VarDef
Expand Down
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {

def selectorText(sel: untpd.ImportSelector): Text =
val id: Text =
if sel.isGiven then keywordText("given") else toText(sel.imported)
if sel.isGiven then keywordText("given")
else sel.imported.name match
case nme.WILDCARD => "*"
case nme.raw.STAR => "`*`"
case name => toText(name)
val rename: Text =
if sel.renamed.isEmpty then "" else Str(" => ") ~ toText(sel.renamed)
if sel.renamed.isEmpty then "" else Str(" as ") ~ toText(sel.renamed)
val bound: Text =
if sel.bound.isEmpty then ""
else if sel.isGiven then Str(" ") ~ toText(sel.bound)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
}

override def transformOther(tree: Tree)(using Context): Tree = tree match {
case tree: ImportOrExport[_] => EmptyTree
case tree: ImportOrExport => EmptyTree
case tree: NamedArg => transformAllDeep(tree.arg)
case tree => if (tree.isType) toTypeTree(tree) else tree
}
Expand Down
20 changes: 10 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ class Namer { typer: Typer =>
// make sure testing contexts are not captured by completers
assert(!ictx.reporter.isInstanceOf[ExploringReporter])

protected def typeSig(sym: Symbol): Type = original match {
protected def typeSig(sym: Symbol): Type = original match
case original: ValDef =>
if (sym.is(Module)) moduleValSig(sym)
else valOrDefDefSig(original, sym, Nil, identity)(using localContext(sym).setNewScope)
Expand All @@ -702,16 +702,12 @@ class Namer { typer: Typer =>
nestedTyper(sym) = typer1
typer1.defDefSig(original, sym)(using localContext(sym).setTyper(typer1))
case imp: Import =>
try {
val expr1 = typedAheadExpr(imp.expr, AnySelectionProto)
try
val expr1 = typedImportQualifier(imp, typedAheadExpr)
ImportType(expr1)
}
catch {
case ex: CyclicReference =>
typr.println(s"error while completing ${imp.expr}")
throw ex
}
}
catch case ex: CyclicReference =>
typr.println(s"error while completing ${imp.expr}")
throw ex

final override def complete(denot: SymDenotation)(using Context): Unit = {
if (Config.showCompletions && ctx.typerState != creationContext.typerState) {
Expand Down Expand Up @@ -987,6 +983,10 @@ class Namer { typer: Typer =>
def exportForwarders(exp: Export): List[tpd.MemberDef] = {
val buf = new mutable.ListBuffer[tpd.MemberDef]
val Export(expr, selectors) = exp
if expr.isEmpty then
report.error(em"Export selector must have prefix and `.`", exp.srcPos)
return Nil

val path = typedAheadExpr(expr, AnySelectionProto)
checkLegalExportPath(path, selectors)
lazy val wildcardBound = importBound(selectors, isGiven = false)
Expand Down
27 changes: 22 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2339,19 +2339,36 @@ class Typer extends Namer
.asInstanceOf[untpd.ImportSelector]
}

def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import = {
val expr1 = typedExpr(imp.expr, AnySelectionProto)
def typedImportQualifier(imp: untpd.Import, typd: (untpd.Tree, Type) => Tree)(using Context): Tree =
if imp.expr == untpd.EmptyTree then
assert(imp.selectors.length == 1, imp)
val from = imp.selectors.head.imported
val sel = tryAlternatively
(typedIdent(from, WildcardType))
(typedIdent(cpy.Ident(from)(from.name.toTypeName), WildcardType))

sel.tpe match
case TermRef(prefix: SingletonType, _) =>
singleton(prefix).withSpan(from.span)
case TypeRef(prefix: SingletonType, _) =>
singleton(prefix).withSpan(from.span)
case _ =>
errorTree(from,
em"""Illegal import selector: $from
|The selector is not a member of an object or package.""")
else typd(imp.expr, AnySelectionProto)

def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import =
val expr1 = typedImportQualifier(imp, typedExpr)
checkLegalImportPath(expr1)
val selectors1 = typedSelectors(imp.selectors)
assignType(cpy.Import(imp)(expr1, selectors1), sym)
}

def typedExport(exp: untpd.Export)(using Context): Export = {
def typedExport(exp: untpd.Export)(using Context): Export =
val expr1 = typedExpr(exp.expr, AnySelectionProto)
// already called `checkLegalExportPath` in Namer
val selectors1 = typedSelectors(exp.selectors)
assignType(cpy.Export(exp)(expr1, selectors1))
}

def typedPackageDef(tree: untpd.PackageDef)(using Context): Tree =
val pid1 = withMode(Mode.InPackageClauseName)(typedExpr(tree.pid, AnySelectionProto))
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class CompilationTests {

aggregateTests(
compileFile("tests/rewrites/rewrites.scala", scala2CompatMode.and("-rewrite", "-indent")),
compileFile("tests/rewrites/rewrites3x.scala", defaultOptions.and("-rewrite", "-source", "3.1-migration")),
compileFile("tests/rewrites/i8982.scala", defaultOptions.and("-indent", "-rewrite")),
compileFile("tests/rewrites/i9632.scala", defaultOptions.and("-indent", "-rewrite"))
).checkRewrites()
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/contributing/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type stealer:
```bash
$ sbt
> repl
scala> import dotty.tools.DottyTypeStealer._; import dotty.tools.dotc.core._; import Contexts._,Types._
scala> import dotty.tools.DottyTypeStealer.*; import dotty.tools.dotc.core.*; import Contexts.*,Types.*
```

Now, you can define types and access their representation. For example:
Expand Down
19 changes: 9 additions & 10 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ type val var while with yield
### Soft keywords

```
derives end extension infix inline opaque open transparent using | * + -
as derives end extension infix inline opaque open transparent using | * + -
```

See the [separate section on soft keywords](./soft-modifier.md) for additional
Expand Down Expand Up @@ -258,8 +258,7 @@ ArgumentExprs ::= ParArgumentExprs
BlockExpr ::= <<< (CaseClauses | Block) >>>
Block ::= {BlockStat semi} [BlockResult] Block(stats, expr?)
BlockStat ::= Import
| {Annotation {nl}} [‘implicit’ | ‘lazy’] Def
| {Annotation {nl}} {LocalModifier} TmplDef
| {Annotation {nl}} {LocalModifier} Def
| Extension
| Expr1
| EndMarker
Expand Down Expand Up @@ -353,16 +352,16 @@ AccessQualifier ::= ‘[’ id ‘]’
Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} Apply(tpe, args)

Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
Export ::= ‘export’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec Import(expr, sels)
ImportSpec ::= id
| ‘_’
| ‘given’
| SimpleRef ‘as’ id Import(EmptyTree, ImportSelector(ref, id))
ImportSpec ::= NamedSelector
| WildcardSelector
| ‘{’ ImportSelectors) ‘}’
ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
NamedSelector ::= id [‘as’ (id | ‘_’)]
WildCardSelector ::= ‘*' | ‘given’ [InfixType]
ImportSelectors ::= NamedSelector [‘,’ ImportSelectors]
| WildCardSelector {‘,’ WildCardSelector}
WildCardSelector ::= ‘given’ [InfixType]
| ‘_'
Export ::= ‘export’ ImportExpr {‘,’ ImportExpr}

EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL
EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ‘try’
Expand Down
Loading