Skip to content

Commit

Permalink
Change wildcard given selectors
Browse files Browse the repository at this point in the history
Implements a syntax change for wildcard imports.
Instead of
```scala
import p.{given _}
```
write
```scala
import p.given
```
The same applies for wildcard given imports in braces: `given _` is contracted to just `given`. Given-by-type imports remain unaffected: It's still
```scala
import p.{given T}
```
for those.

The rationale is that we should not make it needlessly hard to write wildcard given imports. The additional underscore after a `given` is not needed for disambiguation and clutters the code.
  • Loading branch information
odersky committed Oct 6, 2020
1 parent f0bbd95 commit ecdf9bf
Show file tree
Hide file tree
Showing 20 changed files with 48 additions and 41 deletions.
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3094,16 +3094,18 @@ object Parsers {
/** 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) }

/** ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
* | WildCardSelector {‘,’ WildCardSelector}
* WildCardSelector ::= ‘given’ (‘_' | InfixType)
* WildCardSelector ::= ‘given’ [InfixType]
* | ‘_'
*/
def importSelectors(idOK: Boolean): List[ImportSelector] =
Expand All @@ -3114,12 +3116,13 @@ object Parsers {
ImportSelector(wildcardSelectorId())
case GIVEN =>
val start = in.skipToken()
def givenSelector() = atSpan(start) { Ident(nme.EMPTY) }
if in.token == USCORE then
in.nextToken()
ImportSelector(givenSelector()) // Let the selector span all of `given _`; needed for -Ytest-pickler
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 = infixType())
else
ImportSelector(givenSelector(), bound = infixType())
ImportSelector(givenSelectorId(start))
case _ =>
val from = termIdent()
if !idOK then syntaxError(i"named imports cannot follow wildcard imports")
Expand All @@ -3143,6 +3146,8 @@ object Parsers {
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 _ =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ object Tokens extends TokensCommon {
final val canStartStatTokens3: TokenSet = canStartExprTokens3 | mustStartStatTokens | BitSet(
AT, CASE)

final val canEndStatTokens: TokenSet = atomicExprTokens | BitSet(TYPE, RPAREN, RBRACE, RBRACKET, OUTDENT)
final val canEndStatTokens: TokenSet = atomicExprTokens | BitSet(TYPE, GIVEN, RPAREN, RBRACE, RBRACKET, OUTDENT)

/** Tokens that stop a lookahead scan search for a `<-`, `then`, or `do`.
* Used for disambiguating between old and new syntax.
Expand Down
3 changes: 2 additions & 1 deletion docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,10 +348,11 @@ Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec Import(expr, sels)
ImportSpec ::= id
| ‘_’
| ‘given’
| ‘{’ ImportSelectors) ‘}’
ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
| WildCardSelector {‘,’ WildCardSelector}
WildCardSelector ::= ‘given’ (‘_' | InfixType)
WildCardSelector ::= ‘given’ [InfixType]
| ‘_'
Export ::= ‘export’ [‘given’] ImportExpr {‘,’ ImportExpr}
Expand Down
15 changes: 8 additions & 7 deletions docs/docs/reference/contextual/given-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@ object A {

object B {
import A._
import A.{given _}
import A.given
}
```

In the code above, the `import A._` clause of object `B` will import all members
of `A` _except_ the given instance `tc`. Conversely, the second import `import A.{given _}` will import _only_ that given instance.
of `A` _except_ the given instance `tc`. Conversely, the second import `import A.given` will import _only_ that given instance.
The two import clauses can also be merged into one:

```scala
object B {
import A.{given _, _}
import A.{given, _}
}
```

Generally, a normal wildcard selector `_` brings all definitions other than givens or extensions into scope
whereas a `given _` selector brings all givens (including those resulting from extensions) into scope.
whereas a `given` selector brings all givens (including those resulting from extensions) into scope.

There are two main benefits arising from these rules:

Expand Down Expand Up @@ -106,13 +106,13 @@ normal imports to givens and given imports.
The following modifications avoid this hurdle to migration.

1. A `given` import selector also brings old style implicits into scope. So, in Scala 3.0
an old-style implicit definition can be brought into scope either by a `_` or a `given _` wildcard selector.
an old-style implicit definition can be brought into scope either by a `_` or a `given` wildcard selector.

2. In Scala 3.1, old-style implicits accessed through a `_` wildcard import will give a deprecation warning.

3. In some version after 3.1, old-style implicits accessed through a `_` wildcard import will give a compiler error.

These rules mean that library users can use `given _` selectors to access old-style implicits in Scala 3.0,
These rules mean that library users can use `given` selectors to access old-style implicits in Scala 3.0,
and will be gently nudged and then forced to do so in later versions. Libraries can then switch to
given instances once their user base has migrated.

Expand All @@ -123,10 +123,11 @@ Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= StableId ‘.’ ImportSpec
ImportSpec ::= id
| ‘_’
| ‘given’
| ‘{’ ImportSelectors) ‘}’
ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
| WildCardSelector {‘,’ WildCardSelector}
WildCardSelector ::= ‘_'
| ‘given’ (‘_' | InfixType)
| ‘given’ [InfixType]
Export ::= ‘export’ ImportExpr {‘,’ ImportExpr}
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import scala.deriving._
import scala.quoted._
import JsonEncoder.{given _, _}
import JsonEncoder.{given, _}

object SummonJsonEncoderTest {

Expand Down
2 changes: 1 addition & 1 deletion tests/neg-custom-args/impl-conv/B.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package implConv

object B {
import A.{_, given _}
import A.{_, given}

"".foo

Expand Down
2 changes: 1 addition & 1 deletion tests/neg-custom-args/implicit-conversions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object D {
}

object Test {
import D.{given _}
import D.given

val x1: A = new B // error under -Xfatal-warnings -feature
val x2: B = new A // error under -Xfatal-warnings -feature
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/i7048e.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract class Test {

{
val t: Test = this
import t.{given _}
import t.given
println(summon[Type[t.T]].show)
// val r = '{Option.empty[t.T]} // access to value t from wrong staging level
val r2 = '{Option.empty[${t.T}]} // works
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/import-implied.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ object C {
foo(using tc) // ok
}
object D {
import A.{foo, given _}
import A.{foo, given}
foo // ok
foo(using tc) // ok
}
object E {
import A.{_, given _}
import A.{_, given}
foo // ok
foo(using tc) // ok
}
2 changes: 1 addition & 1 deletion tests/pos-macros/i7011/Macros_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import scala.quoted._
inline def mcr(body: => Any): Unit = ${mcrImpl('body)}

def mcrImpl[T](body: Expr[Any])(using ctx: QuoteContext) : Expr[Any] = {
import ctx.tasty.{_, given _}
import ctx.tasty.{_, given}

val bTree = body.unseal
val under = bTree.underlyingArgument
Expand Down
2 changes: 1 addition & 1 deletion tests/pos-macros/i7048e.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract class Test {

{
val t: Test = this
import t.{given _}
import t.given
println(summon[Type[t.T]].show)
// val r = '{Option.empty[t.T]} // access to value t from wrong staging level
val r2 = '{Option.empty[${t.T}]}
Expand Down
2 changes: 1 addition & 1 deletion tests/pos-macros/i8651b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object Macros {
inline def coroutine[T](inline body: Any): Coroutine[T] = ${ coroutineImpl('{body}) }

def coroutineImpl[T: Type](expr: Expr[_ <: Any])(implicit qtx: QuoteContext): Expr[Coroutine[T]] = {
import qtx.tasty.{_, given _}
import qtx.tasty.{_, given}

'{
new Coroutine[T] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ object Macros {


def impl(reflect: Reflection): Unit = {
import reflect.{_, given _}
import reflect.{_, given}

def foo(tree: Tree, term: Term, typeTree: TypeTree, parent: Tree) = {

Expand Down
6 changes: 3 additions & 3 deletions tests/pos/i5978.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ package p1 {

object Testcase {
def main(args: Array[String]): Unit = {
import TextParser.{given _, _}
import TextParser.{given, _}

val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP
val tp_i = summon[TokenParser[Char, Position[CharSequence]]]
Expand All @@ -45,7 +45,7 @@ package p2 {

object Testcase {
def main(args: Array[String]): Unit = {
import TextParser.{given _, _}
import TextParser.{given, _}

val tp_v: TokenParser[Char, Position[CharSequence]] = TextParser.TP
val tp_i = summon[TokenParser[Char, Position[CharSequence]]]
Expand All @@ -63,7 +63,7 @@ package p3 {

object Testcase {
def main(args: Array[String]): Unit = {
import TextParser.{_, given _}
import TextParser.{_, given}

val co_i: Conversion[Char, Position[CharSequence]] = summon[Conversion[Char, Position[CharSequence]]]

Expand Down
2 changes: 1 addition & 1 deletion tests/pos/i7532.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Tasty {

object Foo {
def impl(using tasty: Tasty) : Unit = {
import tasty.{_, given _}
import tasty.{_, given}
val Select() = (??? : Term)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object Macros {
}

class MyTraverser[R <: scala.tasty.Reflection & Singleton](val reflect: R)(buff: StringBuilder) extends scala.tasty.reflect.TreeTraverser {
import reflect.{given _, _}
import reflect.{given, _}
override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
case tree @ DefDef(name, _, _, _, _) =>
Expand Down
2 changes: 1 addition & 1 deletion tests/run-macros/i8007/Test_4.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ enum OptInv[+T] {

@main def Test() = {
import Opt._
import Eq.{given _, _}
import Eq.{given, _}

val t1 = test1(Person("Test", 23))
println(t1)
Expand Down
2 changes: 1 addition & 1 deletion tests/run-macros/tasty-tree-map/quoted_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ object Macros {
implicit inline def identityMaped[T](x: => T): T = ${ impl('x) }

def impl[T: Type](x: Expr[T])(using qctx: QuoteContext) : Expr[T] = {
import qctx.tasty.{_, given _} // FIXME: #8919
import qctx.tasty.{_, given} // FIXME: #8919
val identityMap = new TreeMap { }
val transformed = identityMap.transformTerm(x.unseal).seal.cast[T]
transformed
Expand Down
2 changes: 1 addition & 1 deletion tests/run/exports.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object Test extends App {

object Copier {
val printer = new Printer
export printer.{given _, _}
export printer.{given, _}
export Scanner.{scan => scanIt, _}

val config2 = summon[Config]
Expand Down
20 changes: 10 additions & 10 deletions tests/run/implied-priority.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ object NormalImplicits extends LowPriorityImplicits {
}

def test1 = {
import NormalImplicits.{given _}
import NormalImplicits.given
assert(summon[E[String]].str == "low") // No Arg available, so only t1 applies

{ given Arg[String]
Expand All @@ -43,7 +43,7 @@ object Impl2 {
}

def test2 = {
import Impl2.{given _}
import Impl2.given
assert(summon[E[String]].str == "low") // No Arg available, so only t1 applies

{ given Arg[String]
Expand All @@ -64,8 +64,8 @@ object Impl2a {
}

def test2a = {
import Impl2.{given _}
import Impl2a.{given _}
import Impl2.given
import Impl2a.given

given Arg[String]
assert(summon[E[String]].str == "hi")
Expand All @@ -85,11 +85,11 @@ object Override {
}

def test3 = {
import Impl3.{given _}
import Impl3.given
assert(summon[E[String]].str == "low") // only t1 is available

{ import Override.{given _}
import Impl3.{given _}
{ import Override.given
import Impl3.given
assert(summon[E[String]].str == "hi") // `over` takes priority since its result type is a subtype of t1's.
}
}
Expand All @@ -111,7 +111,7 @@ object fallback4 {
}

def test4 = {
import Impl4.{given _}
import Impl4.given
import fallback4._
assert(withFallback[String].str == "string") // t1 is applicable
assert(withFallback[Int].str == "fallback") // No applicable instances, pick the default
Expand All @@ -138,8 +138,8 @@ object fallback5 {
}

def test5 = {
import Impl4.{given _}
import fallback5.{given _}
import Impl4.given
import fallback5.given

// All inferred terms go through the given instance in fallback5.
// They differ in what implicit argument is synthesized for that instance.
Expand Down

0 comments on commit ecdf9bf

Please sign in to comment.