Skip to content

Commit

Permalink
Implement SIP 64 as non-experimental (#21668)
Browse files Browse the repository at this point in the history
For the new syntax of givens and context bounds, see the diffs in
reference/syntax.md.
  • Loading branch information
odersky authored Oct 4, 2024
2 parents 49d39d9 + df75afc commit ebbd685
Show file tree
Hide file tree
Showing 297 changed files with 1,352 additions and 1,182 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,7 @@ object desugar {
str.toTermName.asSimpleName

/** Extract a synthesized given name from a type tree. This is used for
* both anonymous givens and (under x.modularity) deferred givens.
* both anonymous givens and deferred givens.
* @param followArgs if true include argument types in the name
*/
private class NameExtractor(followArgs: Boolean) extends UntypedTreeAccumulator[String] {
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
case class ExtMethods(paramss: List[ParamClause], methods: List[Tree])(implicit @constructorOnly src: SourceFile) extends Tree
case class ContextBoundTypeTree(tycon: Tree, paramName: TypeName, ownName: TermName)(implicit @constructorOnly src: SourceFile) extends Tree
// `paramName: tycon as ownName`, ownName != EmptyTermName only under x.modularity
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree

case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ enum MigrationVersion(val warnFrom: SourceVersion, val errorFrom: SourceVersion)
case ImportRename extends MigrationVersion(future, future)
case ParameterEnclosedByParenthesis extends MigrationVersion(future, future)
case XmlLiteral extends MigrationVersion(future, future)
case GivenSyntax extends MigrationVersion(future, never)

require(warnFrom.ordinal <= errorFrom.ordinal)

Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/config/SourceVersion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ enum SourceVersion:
// !!! Keep in sync with scala.runtime.stdlibPatches.language !!!
case `future-migration`, `future`

case `never` // needed for MigrationVersion.errorFrom if we never want to issue an error

val isMigrating: Boolean = toString.endsWith("-migration")

def stable: SourceVersion =
Expand All @@ -32,7 +34,7 @@ object SourceVersion extends Property.Key[SourceVersion]:
def defaultSourceVersion = `3.6`

/** language versions that may appear in a language import, are deprecated, but not removed from the standard library. */
val illegalSourceVersionNames = List("3.1-migration").map(_.toTermName)
val illegalSourceVersionNames = List("3.1-migration", "never").map(_.toTermName)

/** language versions that the compiler recognises. */
val validSourceVersionNames = values.toList.map(_.toString.toTermName)
Expand Down
40 changes: 28 additions & 12 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -994,8 +994,8 @@ object Parsers {
skipParams()
lookahead.isColon
&& {
!in.featureEnabled(Feature.modularity)
|| { // with modularity language import, a `:` at EOL after an identifier represents a single identifier given
!sourceVersion.isAtLeast(`3.6`)
|| { // in the new given syntax, a `:` at EOL after an identifier represents a single identifier given
// Example:
// given C:
// def f = ...
Expand Down Expand Up @@ -1833,7 +1833,7 @@ object Parsers {
infixOps(t, canStartInfixTypeTokens, operand, Location.ElseWhere, ParseKind.Type,
isOperator = !followingIsVararg()
&& !isPureArrow
&& !(isIdent(nme.as) && in.featureEnabled(Feature.modularity))
&& !(isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`))
&& nextCanFollowOperator(canStartInfixTypeTokens))

/** RefinedType ::= WithType {[nl] Refinement} [`^` CaptureSet]
Expand Down Expand Up @@ -2226,20 +2226,30 @@ object Parsers {
def contextBound(pname: TypeName): Tree =
val t = toplevelTyp()
val ownName =
if isIdent(nme.as) && in.featureEnabled(Feature.modularity) then
if isIdent(nme.as) && sourceVersion.isAtLeast(`3.6`) then
in.nextToken()
ident()
else EmptyTermName
ContextBoundTypeTree(t, pname, ownName)

/** ContextBounds ::= ContextBound | `{` ContextBound {`,` ContextBound} `}`
/** ContextBounds ::= ContextBound [`:` ContextBounds]
* | `{` ContextBound {`,` ContextBound} `}`
*/
def contextBounds(pname: TypeName): List[Tree] =
if in.isColon then
in.nextToken()
if in.token == LBRACE && in.featureEnabled(Feature.modularity)
if in.token == LBRACE && sourceVersion.isAtLeast(`3.6`)
then inBraces(commaSeparated(() => contextBound(pname)))
else contextBound(pname) :: contextBounds(pname)
else
val bound = contextBound(pname)
val rest =
if in.isColon then
report.errorOrMigrationWarning(
em"Multiple context bounds should be enclosed in `{ ... }`",
in.sourcePos(), MigrationVersion.GivenSyntax)
contextBounds(pname)
else Nil
bound :: rest
else if in.token == VIEWBOUND then
report.errorOrMigrationWarning(
em"view bounds `<%' are no longer supported, use a context bound `:' instead",
Expand Down Expand Up @@ -4014,7 +4024,7 @@ object Parsers {
case SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | OUTDENT | EOF =>
makeTypeDef(typeAndCtxBounds(tname))
case _ if (staged & StageKind.QuotedPattern) != 0
|| in.featureEnabled(Feature.modularity) && in.isColon =>
|| sourceVersion.isAtLeast(`3.6`) && in.isColon =>
makeTypeDef(typeAndCtxBounds(tname))
case _ =>
syntaxErrorOrIncomplete(ExpectedTypeBoundOrEquals(in.token))
Expand Down Expand Up @@ -4189,7 +4199,7 @@ object Parsers {
def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) {
var mods1 = addMod(mods, givenMod)
val nameStart = in.offset
var newSyntaxAllowed = in.featureEnabled(Feature.modularity)
var newSyntaxAllowed = sourceVersion.isAtLeast(`3.6`)
val hasEmbeddedColon = !in.isColon && followingIsGivenDefWithColon()
val name = if isIdent && hasEmbeddedColon then ident() else EmptyTermName

Expand Down Expand Up @@ -4260,6 +4270,9 @@ object Parsers {
in.nextToken()
newSignature()
else if hasEmbeddedColon then
report.errorOrMigrationWarning(
em"This old given syntax is no longer supported; use `=>` instead of `:`",
in.sourcePos(), MigrationVersion.GivenSyntax)
newSyntaxAllowed = false
val tparamsOld = typeParamClauseOpt(ParamOwner.Given)
newLineOpt()
Expand Down Expand Up @@ -4294,10 +4307,10 @@ object Parsers {
if name.isEmpty then
syntaxError(em"Anonymous given cannot be abstract, or maybe you want to define a concrete given and are missing a `()` argument?", in.lastOffset)
if newSyntaxAllowed then
warning(
em"""This defines an abstract given, which is deprecated. Use a `deferred` given instead.
report.errorOrMigrationWarning(
em"""This defines an abstract given, which is no longer supported. Use a `deferred` given instead.
|Or, if you intend to define a concrete given, follow the type with `()` arguments.""",
in.lastOffset)
in.sourcePos(in.lastOffset), MigrationVersion.GivenSyntax)
DefDef(name, adjustDefParams(joinParams(tparams, vparamss)), parents.head, EmptyTree)
else
// structural instance
Expand Down Expand Up @@ -4487,6 +4500,9 @@ object Parsers {

/** with Template, with EOL <indent> interpreted */
def withTemplate(constr: DefDef, parents: List[Tree]): Template =
report.errorOrMigrationWarning(
em"Given member definitions starting with `with` are no longer supported; use `{...}` or `:` followed by newline instead",
in.sourcePos(), MigrationVersion.GivenSyntax)
accept(WITH)
val (self, stats) = templateBody(parents, rewriteWithColon = false)
Template(constr, parents, Nil, self, stats)
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import TypeApplications.*
import NameKinds.{WildcardParamName, DefaultGetterName}
import util.Chars.isOperatorPart
import config.{Config, Feature}
import config.Feature.sourceVersion
import config.SourceVersion.*

import dotty.tools.dotc.util.SourcePosition
import dotty.tools.dotc.ast.untpd.{MemberDef, Modifiers, PackageDef, RefTree, Template, TypeDef, ValOrDefDef}
Expand Down Expand Up @@ -751,7 +753,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case GenAlias(pat, expr) =>
toText(pat) ~ " = " ~ toText(expr)
case ContextBounds(bounds, cxBounds) =>
if Feature.enabled(Feature.modularity) then
if sourceVersion.isAtLeast(`3.6`) then
def boundsText(bounds: Tree) = bounds match
case ContextBoundTypeTree(tpt, _, ownName) =>
toText(tpt) ~ (" as " ~ toText(ownName) `provided` !ownName.isEmpty)
Expand Down
10 changes: 8 additions & 2 deletions docs/_docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ TypeArgs ::= ‘[’ Types ‘]’
Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>> ds
TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi)
TypeAndCtxBounds ::= TypeBounds [‘:’ ContextBounds] ContextBounds(typeBounds, tps)
ContextBounds ::= ContextBound | '{' ContextBound {',' ContextBound} '}'
ContextBounds ::= ContextBound
| ContextBound `:` ContextBounds -- to be deprecated
| '{' ContextBound {',' ContextBound} '}'
ContextBound ::= Type ['as' id]
Types ::= Type {‘,’ Type}
NamesAndTypes ::= NameAndType {‘,’ NameAndType}
Expand Down Expand Up @@ -464,7 +466,7 @@ TypeDef ::= id [HkTypeParamClause] {FunParamClause} TypeAndCtxBounds
TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
| [‘case’] ‘object’ ObjectDef
| ‘enum’ EnumDef
| ‘given’ GivenDef
| ‘given’ (GivenDef | OldGivenDef)
ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ)
ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
ConstrMods ::= {Annotation} [AccessModifier]
Expand All @@ -483,6 +485,10 @@ GivenConditional ::= DefTypeParamClause
| GivenType
GivenType ::= AnnotType1 {id [nl] AnnotType1}
OldGivenDef ::= [OldGivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) -- syntax up to Scala 3.5, to be deprecated in the future
OldGivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody]
Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause}
‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods
ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ trait Codec[T]:

given intCodec: Codec[Int] = ???

given optionCodec[T](using ev: => Codec[T]): Codec[Option[T]] with
given optionCodec: [T] => (ev: => Codec[T]) => Codec[Option[T]]:
def write(xo: Option[T]) = xo match
case Some(x) => ev.write(x)
case None =>
Expand Down
Loading

0 comments on commit ebbd685

Please sign in to comment.