Skip to content

Spec: Syntax changes. #17943

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 8 commits into from
Jun 21, 2023
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions docs/_spec/01-lexical-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ The principle of optional braces is that any keyword that can be followed by `{`

The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md).

´\color{red}{\text{TODO SCALA3: Port soft-modifier.md and link it here.}}´

In the context-free productions below we use the notation `<<< ts >>>` to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent`.
Analogously, the notation `:<<< ts >>>` indicates a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent` that follows a `colon` token.

Expand Down Expand Up @@ -121,12 +119,35 @@ type val var while with yield

Additionally, the following soft keywords are reserved only in some situations.

´\color{red}{\text{TODO SCALA3: Port soft-modifier.md and link it here.}}´

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

A soft modifier is one of the identifiers `infix`, `inline`, `opaque`, `open` and `transparent`.

A soft keyword is a soft modifier, or one of `as`, `derives`, `end`, `extension`, `using`, `|`, `+`, `-`, `*`.

A soft modifier is treated as an actual modifier of a definition if it is followed by a hard modifier or a keyword combination starting a definition (`def`, `val`, `var`, `type`, `given`, `class`, `trait`, `object`, `enum`, `case class`, `case object`).
Between the two words, there may be a sequence of newline tokens and/or other soft modifiers.

Otherwise, soft keywords are treated as actual keywords in the following situations:

- `as`, if it appears in a renaming import clause.
- `derives`, if it appears after an extension clause or after the name and possibly parameters of a class, trait, object, or enum definition.
- `end`, if it appears at the start of a line following a statement (i.e. definition or toplevel expression) and is followed on the same line by a single non-comment token that is:
- one of the keywords `for`, `given`, `if`, `match`, `new`, `this`, `throw`, `try`, `val`, `while`, or
- an identifier.
- `extension`, if it appears at the start of a statement and is followed by `(` or `[`.
- `inline`, if it is followed by any token that can start an expression.
- `using`, if it appears at the start of a parameter or argument list.
- `|`, if it separates two patterns in an alternative.
- `+`, `-`, if they appear in front of a type parameter.
- `*`, if it appears in a wildcard import, or if it follows the type of a parameter, or if it appears in a vararg splice `x*`.

Everywhere else, a soft keyword is treated as a normal identifier.

<!-- -->

> When one needs to access Java identifiers that are reserved words in Scala, use backquote-enclosed strings.
Expand All @@ -143,7 +164,7 @@ Scala is a line-oriented language where statements may be terminated by semi-col
A newline in a Scala source text is treated as the special token “nl” if the three following criteria are satisfied:

1. The token immediately preceding the newline can terminate a statement.
1. The token immediately following the newline can begin a statement.
1. The token immediately following the newline can begin a statement and is not a _leading infix operator_.
1. The token appears in a region where newlines are enabled.

The tokens that can terminate a statement are: literals, identifiers and the following delimiters and reserved words:
Expand All @@ -164,6 +185,14 @@ with yield , . ; : = => <- <: <%
A `case` token can begin a statement only if followed by a
`class` or `object` token.

A _leading infix operator_ is a symbolic identifier such as `+`, or `approx_==`, or an identifier in backticks that:

- starts a new line, and
- is not following a blank line, and
- is followed by at least one whitespace character (including new lines) and a token that can start an expression.

Furthermore, if the operator appears on its own line, the next line must have at least the same indentation width as the operator.

Newlines are enabled in:

1. all of a Scala source file, except for nested regions where newlines are disabled, and
Expand Down
57 changes: 31 additions & 26 deletions docs/_spec/02-identifiers-names-and-scopes.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ The compiler supplies imports in a preamble to every source file.
This preamble conceptually has the following form, where braces indicate nested scopes:

```scala
import java.lang._
import java.lang.*
{
import scala._
import scala.*
{
import Predef._
import Predef.*
{ /* source */ }
}
}
Expand All @@ -95,8 +95,8 @@ This allows redundant type aliases to be imported without introducing an ambigui
object X { type T = annotation.tailrec }
object Y { type T = annotation.tailrec }
object Z {
import X._, Y._, annotation.{tailrec => T} // OK, all T mean tailrec
@T def f: Int = { f ; 42 } // error, f is not tail recursive
import X.*, Y.*, annotation.tailrec as T // OK, all T mean tailrec
@T def f: Int = { f ; 42 } // error, f is not tail recursive
}
```

Expand All @@ -107,7 +107,7 @@ Similarly, imported aliases of names introduced by package statements are allowe
package p { class C }

// xy.scala
import p._
import p.*
package p { class X extends C }
package q { class Y extends C }
```
Expand All @@ -132,27 +132,32 @@ package q {
The following program illustrates different kinds of bindings and precedences between them.

```scala
package p { // `X' bound by package clause
import Console._ // `println' bound by wildcard import
object Y {
println(s"L4: $X") // `X' refers to `p.X' here
locally {
import q._ // `X' bound by wildcard import
println(s"L7: $X") // `X' refers to `q.X' here
import X._ // `x' and `y' bound by wildcard import
println(s"L9: $x") // `x' refers to `q.X.x' here
package p { // `X' bound by package clause
import Console.* // `println' bound by wildcard import
object Y {
println(s"L4: $X") // `X' refers to `p.X' here
locally {
val x = 3 // `x' bound by local definition
println(s"L12: $x") // `x' refers to constant `3' here
import q.* // `X' bound by wildcard import
println(s"L7: $X") // `X' refers to `q.X' here
import X.* // `x' and `y' bound by wildcard import
println(s"L9: $x") // `x' refers to `q.X.x' here
locally {
import q.X._ // `x' and `y' bound by wildcard import
// println(s"L15: $x") // reference to `x' is ambiguous here
import X.y // `y' bound by explicit import
println(s"L17: $y") // `y' refers to `q.X.y' here
val x = 3 // `x' bound by local definition
println(s"L12: $x") // `x' refers to constant `3' here
locally {
val x = "abc" // `x' bound by local definition
import p.X._ // `x' and `y' bound by wildcard import
// println(s"L21: $y") // reference to `y' is ambiguous here
println(s"L22: $x") // `x' refers to string "abc" here
}}}}}}
import q.X.* // `x' and `y' bound by wildcard import
// println(s"L15: $x") // reference to `x' is ambiguous here
import X.y // `y' bound by explicit import
println(s"L17: $y") // `y' refers to `q.X.y' here
locally {
val x = "abc" // `x' bound by local definition
import p.X.* // `x' and `y' bound by wildcard import
// println(s"L21: $y") // reference to `y' is ambiguous here
println(s"L22: $x") // `x' refers to string "abc" here
}
}
}
}
}
}
```
5 changes: 5 additions & 0 deletions docs/_spec/03-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ Type operators ending in a colon ‘:’ are right-associative; all other operat
In a sequence of consecutive type infix operations ´t_0 \, \mathit{op} \, t_1 \, \mathit{op_2} \, ... \, \mathit{op_n} \, t_n´, all operators ´\mathit{op}\_1, ..., \mathit{op}\_n´ must have the same associativity.
If they are all left-associative, the sequence is interpreted as ´(... (t_0 \mathit{op_1} t_1) \mathit{op_2} ...) \mathit{op_n} t_n´, otherwise it is interpreted as ´t_0 \mathit{op_1} (t_1 \mathit{op_2} ( ... \mathit{op_n} t_n) ...)´.

Under `-source:future`, if the type name is alphanumeric and the target type is not marked [`infix`](./05-classes-and-objects.html#infix), a deprecation warning is emitted.

The type operators `|` and `&` are not really special.
Nevertheless, unless shadowed, they resolve to [the fundamental type aliases `scala.|` and `scala.&`](./12-the-scala-standard-library.html#fundamental-type-aliases), which represent [union and intersection types](#union-and-intersection-types), respectively.

Expand Down Expand Up @@ -493,6 +495,9 @@ All parameterized class types are value types.
In the concrete syntax of wildcard type arguments, if both bounds are omitted, the real bounds are inferred from the bounds of the corresponding type parameter in the target type constructor (which must be concrete).
If only one bound is omitted, `Nothing` or `Any` is used, as usual.

Also in the concrete syntax, `_` can be used instead of `?` for compatibility reasons, with the same meaning.
This alternative will be deprecated in the future, and is already deprecated under `-source:future`.

#### Simplification Rules

Wildcard type arguments used in covariant or contravariant positions can always be simplified to regular types.
Expand Down
86 changes: 57 additions & 29 deletions docs/_spec/04-basic-declarations-and-definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ A variable definition `var ´p´ = ´e´` where ´p´ is a pattern other than a

The name of any declared or defined variable may not end in `_=`.

A variable definition `var ´x´: ´T´ = _` can appear only as a member of a template.
The right-hand-side of a mutable variable definition that is a member of a template can be the special reference `scala.compiletime.uninitialized`: `var ´x´: ´T´ = scala.compiletime.uninitialized`.
It introduces a mutable field with type ´T´ and a default initial value.
The default value depends on the type ´T´ as follows:

Expand All @@ -178,6 +178,9 @@ The default value depends on the type ´T´ as follows:
|`()` | `Unit` |
|`null` | all other types |

`scala.compiletime.uninitialized` can never appear anywhere else.
For compatibility with Scala 2, the syntax `var ´x´: ´T´ = _` is accepted as equivalent to using `uninitialized`.

When they occur as members of a template, both forms of variable definition also introduce a getter method ´x´ which returns the value currently assigned to the variable, as well as a setter method `´x´_=` which changes the value currently assigned to the variable.
The methods have the same signatures as for a variable declaration.
The template then has these getter and setter methods as members, whereas the original variable cannot be accessed directly as a template member.
Expand Down Expand Up @@ -572,6 +575,9 @@ The scope of a type parameter includes the whole signature, including any of the

A _value parameter clause_ ´\mathit{ps}´ consists of zero or more formal parameter bindings such as `´x´: ´T´` or `´x: T = e´`, which bind value parameters and associate them with their types.

A unary operator must not have explicit parameter lists even if they are empty.
A unary operator is a method named `"unary_´op´"` where ´op´ is one of `+`, `-`, `!`, or `~`.

### Default Arguments

Each value parameter declaration may optionally define a default argument.
Expand Down Expand Up @@ -729,64 +735,86 @@ completely. It is an error if the types of two alternatives ´T_i´ and

## Import Clauses

```ebnf
Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors)
ImportSelectors ::= ‘{’ {ImportSelector ‘,’}
(ImportSelector | ‘_’) ‘}’
ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’]
```
Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpecifier
| SimpleRef `as` id
Copy link
Member

@bishabosha bishabosha Jun 20, 2023

Choose a reason for hiding this comment

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

not seen SimpleRef on the lexical syntax page, but it is in the Types page

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that's normal. SimpleRef is a syntax-level non-terminal. The lexical syntax page only shows lexer-level tokens.

ImportSpecifier ::= NamedSelector
| WildcardSelector
| ‘{’ ImportSelectors ‘}’
NamedSelector ::= id [(‘as’ | ’=>’) (id | ‘_’)]
WildcardSelector ::= ‘*’ | ’_’ | ‘given’ [InfixType]
ImportSelectors ::= NamedSelector [‘,’ ImportSelectors]
| WildCardSelector {‘,’ WildcardSelector}
```

- In a `NamedSelector`, `=>` can only be used when inside an `ImportSelectors` and is then equivalent to `as`, to be deprecated in the future.
- In a `WildcardSelector`, `_` is equivalent to `*`, to be deprecated in the future.

An `ImportSpecifier` that is a single `NamedSelector` or `WildcardSelector` is equivalent to an `‘{‘ ImportSelectors ‘}‘` list with that single selector.

An import clause with multiple import expressions `import ´p_1´.´I_1, ..., p_n´.´I_n´` is interpreted as a sequence of import clauses `import ´p_1´.´I_1´; ...; import ´p_n´.´I_n´`.

An import clause has the form `import ´p´.´I´` where ´p´ is a [stable identifier](03-types.html#paths) and ´I´ is an import expression.
The import expression determines a set of names of importable members of ´p´ which are made available without qualification.
An import clause with a single import expression has the form `import ´p´.´I´` where ´p´ is a [prefix](03-types.html#designator-types) and ´I´ is an import specifier.
The import specifier determines a set of names of importable members of ´p´ which are made available without qualification as well as a set of importable `given` members which are made available in the implicit scope.
A member ´m´ of ´p´ is _importable_ if it is [accessible](05-classes-and-objects.html#modifiers).
The most general form of an import expression is a list of _import selectors_
The most general form of an import specifier is a list of _import selectors_

```scala
{ ´x_1´ => ´y_1, ..., x_n´ => ´y_n´, _ }
{ ´x_1´ as ´y_1, ..., x_n´ as ´y_n´, *, given ´T_1´, ..., given ´T_m´, given }
```

for ´n \geq 0´, where the final wildcard `‘_’` may be absent.
It makes available each importable member `´p´.´x_i´` under the unqualified name ´y_i´. I.e. every import selector `´x_i´ => ´y_i´` renames `´p´.´x_i´` to
´y_i´.
If a final wildcard is present, all importable members ´z´ of ´p´ other than `´x_1, ..., x_n,y_1, ..., y_n´` are also made available under their own unqualified names.
for ´n \geq 0´ and ´m \geq 0´, where the wildcards `‘*’` and `’given’` may be absent.
They are decomposed into non-given selectors and given selectors.

### Non-given Imports

Import selectors work in the same way for type and term members.
For instance, an import clause `import ´p´.{´x´ => ´y\,´}` renames the term
name `´p´.´x´` to the term name ´y´ and the type name `´p´.´x´` to the type name ´y´.
Non-given selectors make available each importable member `´p´.´x_i´` under the unqualified name ´y_i´.
In other words, every import selector `´x_i´ as ´y_i´` renames `´p´.´x_i´` to ´y_i´.
When `as ´y_i´` is omitted, ´y_i´ is assumed to be ´x_i´.
If a final wildcard `‘*’` is present, all non-`given` importable members ´z´ of ´p´ other than `´x_1, ..., x_n, y_1, ..., y_n´` are also made available under their own unqualified names.

Non-given import selectors work in the same way for type and term members.
For instance, an import clause `import ´p´.´x´ as ´y´` renames the term name `´p´.´x´` to the term name ´y´ and the type name `´p´.´x´` to the type name ´y´.
At least one of these two names must reference an importable member of ´p´.

If the target in an import selector is a wildcard, the import selector hides access to the source member.
For instance, the import selector `´x´ => _` “renames” ´x´ to the wildcard symbol (which is unaccessible as a name in user programs), and thereby effectively prevents unqualified access to ´x´.
If the target in an import selector is an underscore `as _`, the import selector hides access to the source member instead of importing it.
For instance, the import selector `´x´ as _` “renames” ´x´ to the underscore symbol (which is not accessible as a name in user programs), and thereby effectively prevents unqualified access to ´x´.
This is useful if there is a final wildcard in the same import selector list, which imports all members not mentioned in previous import selectors.

The scope of a binding introduced by an import-clause starts immediately after the import clause and extends to the end of the enclosing block, template, package clause, or compilation unit, whichever comes first.
The scope of a binding introduced by a non-given import clause starts immediately after the import clause and extends to the end of the enclosing block, template, package clause, or compilation unit, whichever comes first.

Several shorthands exist. An import selector may be just a simple name ´x´.
In this case, ´x´ is imported without renaming, so the import selector is equivalent to `´x´ => ´x´`.
Furthermore, it is possible to replace the whole import selector list by a single identifier or wildcard.
The import clause `import ´p´.´x´` is equivalent to `import ´p´.{´x\,´}`, i.e. it makes available without qualification the member ´x´ of ´p´. The import clause `import ´p´._` is equivalent to `import ´p´.{_}`, i.e. it makes available without qualification all members of ´p´ (this is analogous to `import ´p´.*` in Java).
### Given Imports

An import clause with multiple import expressions `import ´p_1´.´I_1, ..., p_n´.´I_n´` is interpreted as a sequence of import clauses `import ´p_1´.´I_1´; ...; import ´p_n´.´I_n´`.
Given selectors make available in the implicit scope all the importable `given` and `implicit` members `´p´.´x´` such that `´p.x´` is a subtype of ´T_i´.
A bare `given` selector without type is equivalent to `given scala.Any`.

The names of the given members are irrelevant for the selection, and are not made available in the normal scope of unqualified names.

###### Example
Consider the object definition:

```scala
object M {
def z = 0, one = 1
def z = 0
def one = 1
def add(x: Int, y: Int): Int = x + y
}
```

Then the block

```scala
{ import M.{one, z => zero, _}; add(zero, one) }
{
import M.{one, z as zero, *}
add(zero, one)
}
```

is equivalent to the block

```scala
{ M.add(M.z, M.one) }
{
M.add(M.z, M.one)
}
```
Loading