diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 4294ade7501f..b4b50dda35b7 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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] = @@ -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 = rejectWildcardType(infixType())) else - ImportSelector(givenSelector(), bound = infixType()) + ImportSelector(givenSelectorId(start)) case _ => val from = termIdent() if !idOK then syntaxError(i"named imports cannot follow wildcard imports") @@ -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 _ => diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index e961f80c0c94..70dce5b85e13 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -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. diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index f8c2915cdf07..9819bd4ee770 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -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} diff --git a/docs/docs/reference/contextual/given-imports.md b/docs/docs/reference/contextual/given-imports.md index 69c2b55e1bf1..c2e3a8e48f2a 100644 --- a/docs/docs/reference/contextual/given-imports.md +++ b/docs/docs/reference/contextual/given-imports.md @@ -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: @@ -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. @@ -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} ``` diff --git a/tests/disabled/pos-macros/i7853/SummonJsonEncoderTest_2.scala b/tests/disabled/pos-macros/i7853/SummonJsonEncoderTest_2.scala index 4891596bfa80..9c51ec6e9c36 100644 --- a/tests/disabled/pos-macros/i7853/SummonJsonEncoderTest_2.scala +++ b/tests/disabled/pos-macros/i7853/SummonJsonEncoderTest_2.scala @@ -1,6 +1,6 @@ import scala.deriving._ import scala.quoted._ -import JsonEncoder.{given _, _} +import JsonEncoder.{given, _} object SummonJsonEncoderTest { diff --git a/tests/neg-custom-args/impl-conv/B.scala b/tests/neg-custom-args/impl-conv/B.scala index 219b0617c563..45a51f28daf8 100644 --- a/tests/neg-custom-args/impl-conv/B.scala +++ b/tests/neg-custom-args/impl-conv/B.scala @@ -1,7 +1,7 @@ package implConv object B { - import A.{_, given _} + import A.{_, given} "".foo diff --git a/tests/neg-custom-args/implicit-conversions.scala b/tests/neg-custom-args/implicit-conversions.scala index 95148ff72344..fd75ca6c9e2d 100644 --- a/tests/neg-custom-args/implicit-conversions.scala +++ b/tests/neg-custom-args/implicit-conversions.scala @@ -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 diff --git a/tests/neg-macros/i7048e.scala b/tests/neg-macros/i7048e.scala index 1130b1bd601b..1ad1891afab6 100644 --- a/tests/neg-macros/i7048e.scala +++ b/tests/neg-macros/i7048e.scala @@ -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 diff --git a/tests/neg/import-implied.scala b/tests/neg/import-given.scala similarity index 79% rename from tests/neg/import-implied.scala rename to tests/neg/import-given.scala index 0262960bf7e5..9aebd770ea31 100644 --- a/tests/neg/import-implied.scala +++ b/tests/neg/import-given.scala @@ -16,12 +16,15 @@ 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 } +object F: + import A.{given ?} // error: unbound wildcard type + diff --git a/tests/pos-macros/i7011/Macros_1.scala b/tests/pos-macros/i7011/Macros_1.scala index f2a10babf8ca..1808c0c1f34e 100644 --- a/tests/pos-macros/i7011/Macros_1.scala +++ b/tests/pos-macros/i7011/Macros_1.scala @@ -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 diff --git a/tests/pos-macros/i7048e.scala b/tests/pos-macros/i7048e.scala index 145f88b5301e..5d08d59dd841 100644 --- a/tests/pos-macros/i7048e.scala +++ b/tests/pos-macros/i7048e.scala @@ -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}]} diff --git a/tests/pos-macros/i8651b.scala b/tests/pos-macros/i8651b.scala index 2ee40e81acc5..23739c604bd4 100644 --- a/tests/pos-macros/i8651b.scala +++ b/tests/pos-macros/i8651b.scala @@ -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] { diff --git a/tests/pos-special/fatal-warnings/tasty-parent-unapply.scala b/tests/pos-special/fatal-warnings/tasty-parent-unapply.scala index 85e64f5e137b..382b79a18fbb 100644 --- a/tests/pos-special/fatal-warnings/tasty-parent-unapply.scala +++ b/tests/pos-special/fatal-warnings/tasty-parent-unapply.scala @@ -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) = { diff --git a/tests/pos/i5978.scala b/tests/pos/i5978.scala index d52d9af19973..75573f775312 100644 --- a/tests/pos/i5978.scala +++ b/tests/pos/i5978.scala @@ -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]]] @@ -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]]] @@ -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]]] diff --git a/tests/pos/i7532.scala b/tests/pos/i7532.scala index 128c639fe723..0ded97411b2c 100644 --- a/tests/pos/i7532.scala +++ b/tests/pos/i7532.scala @@ -12,7 +12,7 @@ class Tasty { object Foo { def impl(using tasty: Tasty) : Unit = { - import tasty.{_, given _} + import tasty.{_, given} val Select() = (??? : Term) } } \ No newline at end of file diff --git a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners/quoted_1.scala b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners/quoted_1.scala index 03d9c9830cca..bb5289b964ce 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners/quoted_1.scala +++ b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners/quoted_1.scala @@ -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, _, _, _, _) => diff --git a/tests/run-macros/i8007/Test_4.scala b/tests/run-macros/i8007/Test_4.scala index a16266fe6fd6..742a1c6e3dea 100644 --- a/tests/run-macros/i8007/Test_4.scala +++ b/tests/run-macros/i8007/Test_4.scala @@ -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) diff --git a/tests/run-macros/tasty-tree-map/quoted_1.scala b/tests/run-macros/tasty-tree-map/quoted_1.scala index de2381cae8cd..654b1a2a53bd 100644 --- a/tests/run-macros/tasty-tree-map/quoted_1.scala +++ b/tests/run-macros/tasty-tree-map/quoted_1.scala @@ -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 diff --git a/tests/run/exports.scala b/tests/run/exports.scala index c82143192152..e3bca4329935 100644 --- a/tests/run/exports.scala +++ b/tests/run/exports.scala @@ -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] diff --git a/tests/run/implied-priority.scala b/tests/run/implied-priority.scala index a1e3a32981ee..5f3a514bcc03 100644 --- a/tests/run/implied-priority.scala +++ b/tests/run/implied-priority.scala @@ -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] @@ -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] @@ -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") @@ -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. } } @@ -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 @@ -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.