Skip to content

Change wildcard given selectors #9949

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 4 commits into from
Oct 6, 2020
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
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
Copy link
Member

Choose a reason for hiding this comment

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

Is this code path still needed for compatibility? If so, should we issue a deprecation warning here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, far, we were changing syntax without warnings. But maybe we should change that now...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried to deprecate but that does not work for the current cycle since we still use given _ in bootstrap code. So we could deprecate at the earliest once 3.0 M1 is released. Or we just rip off the bandaid and disallow it altogether for M2.

Copy link
Member

Choose a reason for hiding this comment

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

We can do a bootstrap before M1 is released (and we probably should do one to make sure everything works OK with the new naming scheme)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK. We can deprecate again once that's done

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