From e06b8318f000a727c9c054801ed8d465a6ee5c29 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 11 Feb 2024 20:10:52 +0100 Subject: [PATCH 1/5] Update syntax.md 1. Add missing soft modifiers 2. Port changes from multiple type parameter sections to reference. Now that multiple type parameter sections are merged, this is the official syntax. This also covers extension method declarations. Fixes #19667 Fixes #19668 --- docs/_docs/internals/syntax.md | 42 +++++++++++++++++++++++----------- docs/_docs/reference/syntax.md | 20 ++++++++-------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index aa8cd15f00a0..ede6144afca3 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -20,6 +20,8 @@ productions map to AST nodes. The following description of Scala tokens uses literal characters `‘c’` when referring to the ASCII fragment `\u0000` – `\u007F`. +Informal descriptions are typeset as `“some comment”`. + ## Lexical Syntax The lexical syntax of Scala is given by the following grammar in EBNF form: @@ -99,7 +101,10 @@ semi ::= ‘;’ | nl {nl} ## Optional Braces -The lexical analyzer also inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](../reference/other-new-features/indentation.md) +The principle of optional braces is that any keyword that can be followed by `{` can also be followed by an indented block, without needing an intervening `:`. +(Allowing an optional `:` would be counterproductive since it would introduce several ways to do the same thing.) + +The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). 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 @@ -181,7 +186,7 @@ FunTypeArgs ::= InfixType | ‘(’ [ FunArgTypes ] ‘)’ | FunParamClause FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ -TypedFunParam ::= [`erased`] id ‘:’ Type +TypedFunParam ::= [`erased`] id ‘:’ IntoType MatchType ::= InfixType `match` <<< TypeCaseClauses >>> InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2) RefinedType ::= AnnotType {[nl] Refinement} RefinedTypeTree(t, ds) @@ -201,14 +206,17 @@ SimpleType1 ::= id Singleton ::= SimpleRef | SimpleLiteral | Singleton ‘.’ id -FunArgType ::= [`erased`] Type - | [`erased`] ‘=>’ Type PrefixOp(=>, t) +FunArgType ::= IntoType + | ‘=>’ IntoType PrefixOp(=>, t) FunArgTypes ::= FunArgType { ‘,’ FunArgType } ParamType ::= [‘=>’] ParamValueType -ParamValueType ::= [‘into’] ExactParamType Into(t) -ExactParamType ::= ParamValueType [‘*’] PostfixOp(t, "*") +ParamValueType ::= IntoType [‘*’] PostfixOp(t, "*") +IntoType ::= [‘into’] IntoTargetType Into(t) + | ‘(’ ‘into’ IntoTargetType ‘)’ +IntoTargetType ::= Type + | FunTypeArgs (‘=>’ | ‘?=>’) IntoType TypeArgs ::= ‘[’ Types ‘]’ ts -Refinement ::= :<<< [RefineDef] {semi [RefineDef]} >>> ds +Refinement ::= :<<< [RefineDef] {semi [RefineDcl]} >>> ds TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi) TypeParamBounds ::= TypeBounds {‘:’ Type} ContextBounds(typeBounds, tps) Types ::= Type {‘,’ Type} @@ -223,7 +231,7 @@ BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block | HkTypeParamClause ‘=>’ Block | Expr1 FunParams ::= Bindings - | [`erased`] id + | id | ‘_’ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] If(Parens(cond), thenp, elsep?) | [‘inline’] ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] If(cond, thenp, elsep?) @@ -272,7 +280,7 @@ ColonArgument ::= colon [LambdaStart] LambdaStart ::= FunParams (‘=>’ | ‘?=>’) | HkTypeParamClause ‘=>’ Quoted ::= ‘'’ ‘{’ Block ‘}’ - | ‘'’ ‘[’ Type ‘]’ + | ‘'’ ‘[’ TypeBlock ‘]’ ExprSplice ::= spliceId -- if inside quoted block | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern | ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern @@ -294,6 +302,8 @@ BlockStat ::= Import | Extension | Expr1 | EndMarker +TypeBlock ::= {TypeBlockStat semi} Type +TypeBlockStat ::= ‘type’ {nl} TypeDcl ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr ForYield(enums, expr) / ForDo(enums, expr) | ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr @@ -376,8 +386,8 @@ Param ::= id ‘:’ ParamType [‘=’ Expr] ### Bindings and Imports ```ebnf -Bindings ::= ‘(’[`erased`] [Binding {‘,’ [`erased`] Binding}] ‘)’ -Binding ::= (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree) +Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ +Binding ::= [`erased`] (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree) Modifier ::= LocalModifier | AccessModifier @@ -390,6 +400,10 @@ LocalModifier ::= ‘abstract’ | ‘implicit’ | ‘lazy’ | ‘inline’ + | ‘transparent’ + | ‘infix’ + | ‘erased’ + AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] AccessQualifier ::= ‘[’ id ‘]’ @@ -414,9 +428,11 @@ EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ### Definitions ```ebnf -RefineDef ::= ‘val’ ValDef - | ‘def’ DefDef +RefineDcl ::= ‘val’ ValDcl + | ‘def’ DefDcl | ‘type’ {nl} TypeDef +ValDcl ::= ids ‘:’ Type +DefDcl ::= DefSig ‘:’ Type Def ::= ‘val’ PatDef | ‘var’ PatDef diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index bf2c27d57863..be51d1f3db1a 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -383,6 +383,8 @@ LocalModifier ::= ‘abstract’ | ‘implicit’ | ‘lazy’ | ‘inline’ + | ‘transparent’ + | ‘infix’ AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] AccessQualifier ::= ‘[’ id ‘]’ @@ -409,24 +411,22 @@ EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ``` RefineDcl ::= ‘val’ ValDcl | ‘def’ DefDcl - | ‘type’ {nl} TypeDcl -Dcl ::= RefineDcl - | ‘var’ VarDcl + | ‘type’ {nl} TypeDef ValDcl ::= ids ‘:’ Type -VarDcl ::= ids ‘:’ Type DefDcl ::= DefSig ‘:’ Type -DefSig ::= id [DefTypeParamClause] [TypelessClauses] [DefImplicitClause] -TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds Def ::= ‘val’ PatDef | ‘var’ PatDef | ‘def’ DefDef | ‘type’ {nl} TypeDcl | TmplDef -PatDef ::= ids [‘:’ Type] ‘=’ Expr - | Pattern2 [‘:’ Type] ‘=’ Expr -DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr +PatDef ::= ids [‘:’ Type] [‘=’ Expr] + | Pattern2 [‘:’ Type] [‘=’ Expr] PatDef(_, pats, tpe?, expr) +DefDef ::= DefSig [‘:’ Type] [‘=’ Expr] DefDef(_, name, paramss, tpe, expr) + | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr DefDef(_, , vparamss, EmptyTree, expr | Block) +DefSig ::= id [DefParamClauses] [DefImplicitClause] +TypeDef ::= id [TypeParamClause] {FunParamClause} TypeBounds TypeDefTree(_, name, tparams, bound + [‘=’ Type] TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef From 6a9bea8138bb2c307d61571cd0b9120ddfa8c404 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 11 Feb 2024 21:28:09 +0100 Subject: [PATCH 2/5] Align Parser with syntax - Change doc comments to match described syntax - Refactor `typ` method to make it clear it matches the syntax --- .../dotty/tools/dotc/parsing/Parsers.scala | 174 +++++++++--------- .../dotty/tools/dotc/transform/Recheck.scala | 3 +- docs/_docs/internals/syntax.md | 10 +- 3 files changed, 90 insertions(+), 97 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6892dfdd94ca..13e30fc04890 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1532,13 +1532,15 @@ object Parsers { * PolyFunType ::= HKTypeParamClause '=>' Type * | HKTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions * FunTypeArgs ::= InfixType - * | `(' [ [ ‘['erased'] FunArgType {`,' FunArgType } ] `)' - * | '(' [ ‘['erased'] TypedFunParam {',' TypedFunParam } ')' + * | `(' [ FunArgType {`,' FunArgType } ] `)' + * | '(' [ TypedFunParam {',' TypedFunParam } ')' + * MatchType ::= InfixType `match` <<< TypeCaseClauses >>> */ def typ(): Tree = val start = in.offset var imods = Modifiers() var erasedArgs: ListBuffer[Boolean] = ListBuffer() + def functionRest(params: List[Tree]): Tree = val paramSpan = Span(start, in.lastOffset) atSpan(start, in.offset) { @@ -1567,7 +1569,8 @@ object Parsers { else accept(ARROW) - val resultType = if isPure then capturesAndResult(typ) else typ() + val resultType = + if isPure then capturesAndResult(typ) else typ() if token == TLARROW then for case ValDef(_, tpt, _) <- params do if isByNameType(tpt) then @@ -1585,98 +1588,93 @@ object Parsers { Function(params, resultType) } - var isValParamList = false + def typeRest(t: Tree) = in.token match + case ARROW | CTXARROW => + erasedArgs.addOne(false) + functionRest(t :: Nil) + case MATCH => + matchType(t) + case FORSOME => + syntaxError(ExistentialTypesNoLongerSupported()) + t + case _ if isPureArrow => + erasedArgs.addOne(false) + functionRest(t :: Nil) + case _ => + if erasedArgs.contains(true) && !t.isInstanceOf[FunctionWithMods] then + syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start)) + t - val t = - if (in.token == LPAREN) { + var isValParamList = false + if in.token == LPAREN then + in.nextToken() + if in.token == RPAREN then in.nextToken() - if (in.token == RPAREN) { - in.nextToken() - functionRest(Nil) - } - else { - val paramStart = in.offset - def addErased() = - erasedArgs.addOne(isErasedKw) - if isErasedKw then { in.skipToken(); } - addErased() - val ts = in.currentRegion.withCommasExpected { + functionRest(Nil) + else + val paramStart = in.offset + def addErased() = + erasedArgs.addOne(isErasedKw) + if isErasedKw then in.skipToken() + addErased() + val args = + in.currentRegion.withCommasExpected: funArgType() match case Ident(name) if name != tpnme.WILDCARD && in.isColon => isValParamList = true - def funParam(start: Offset, mods: Modifiers) = { - atSpan(start) { + def funParam(start: Offset, mods: Modifiers) = + atSpan(start): addErased() typedFunParam(in.offset, ident(), imods) - } - } commaSeparatedRest( typedFunParam(paramStart, name.toTermName, imods), () => funParam(in.offset, imods)) case t => - def funParam() = { - addErased() - funArgType() - } - commaSeparatedRest(t, funParam) - } - accept(RPAREN) - if isValParamList || in.isArrow || isPureArrow then - functionRest(ts) - else { - val ts1 = ts.mapConserve { t => - if isByNameType(t) then - syntaxError(ByNameParameterNotSupported(t), t.span) - stripByNameType(t) - else - t - } - val tuple = atSpan(start) { makeTupleOrParens(ts1) } - infixTypeRest( - refinedTypeRest( - withTypeRest( - annotTypeRest( - simpleTypeRest(tuple))))) - } - } - } - else if (in.token == LBRACKET) { - val start = in.offset - val tparams = typeParamClause(ParamOwner.TypeParam) - if (in.token == TLARROW) - atSpan(start, in.skipToken())(LambdaTypeTree(tparams, toplevelTyp())) - else if (in.token == ARROW || isPureArrow(nme.PUREARROW)) { - val arrowOffset = in.skipToken() - val body = toplevelTyp() - atSpan(start, arrowOffset) { - getFunction(body) match { - case Some(f) => - PolyFunction(tparams, body) - case None => - syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset) - Ident(nme.ERROR.toTypeName) - } - } - } - else { accept(TLARROW); typ() } - } - else if (in.token == INDENT) enclosed(INDENT, typ()) - else infixType() - - in.token match - case ARROW | CTXARROW => - erasedArgs.addOne(false) - functionRest(t :: Nil) - case MATCH => matchType(t) - case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t - case _ => - if isPureArrow then - erasedArgs.addOne(false) - functionRest(t :: Nil) + def funArg() = + addErased() + funArgType() + commaSeparatedRest(t, funArg) + accept(RPAREN) + if isValParamList || in.isArrow || isPureArrow then + functionRest(args) else - if (erasedArgs.contains(true) && !t.isInstanceOf[FunctionWithMods]) - syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start)) - t + val args1 = args.mapConserve: t => + if isByNameType(t) then + syntaxError(ByNameParameterNotSupported(t), t.span) + stripByNameType(t) + else + t + val tuple = atSpan(start): + makeTupleOrParens(args1) + typeRest: + infixTypeRest: + refinedTypeRest: + withTypeRest: + annotTypeRest: + simpleTypeRest(tuple) + else if in.token == LBRACKET then + val start = in.offset + val tparams = typeParamClause(ParamOwner.TypeParam) + if in.token == TLARROW then + atSpan(start, in.skipToken()): + LambdaTypeTree(tparams, toplevelTyp()) + else if in.token == ARROW || isPureArrow(nme.PUREARROW) then + val arrowOffset = in.skipToken() + val body = toplevelTyp() + atSpan(start, arrowOffset): + getFunction(body) match + case Some(f) => + PolyFunction(tparams, body) + case None => + syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset) + Ident(nme.ERROR.toTypeName) + else + accept(TLARROW) + typ() + else if in.token == INDENT then + enclosed(INDENT, typ()) + else + typeRest(infixType()) end typ private def makeKindProjectorTypeDef(name: TypeName): TypeDef = { @@ -1713,7 +1711,7 @@ object Parsers { private def implicitKwPos(start: Int): Span = Span(start, start + nme.IMPLICITkw.asSimpleName.length) - /** TypedFunParam ::= id ':' Type */ + /** TypedFunParam ::= [`erased`] id ':' Type */ def typedFunParam(start: Offset, name: TermName, mods: Modifiers = EmptyModifiers): ValDef = atSpan(start) { acceptColon() @@ -2068,7 +2066,7 @@ object Parsers { */ def paramType(): Tree = paramTypeOf(paramValueType) - /** ParamValueType ::= [`into`] Type [`*'] + /** ParamValueType ::= Type [`*'] */ def paramValueType(): Tree = { val t = maybeInto(toplevelTyp) @@ -2425,7 +2423,7 @@ object Parsers { Match(t, inBracesOrIndented(caseClauses(() => caseClause()))) } - /** `match' `{' TypeCaseClauses `}' + /** `match' <<< TypeCaseClauses >>> */ def matchType(t: Tree): MatchTypeTree = atSpan(startOffset(t), accept(MATCH)) { @@ -2435,7 +2433,7 @@ object Parsers { /** FunParams ::= Bindings * | id * | `_' - * Bindings ::= `(' [[‘erased’] Binding {`,' Binding}] `)' + * Bindings ::= `(' [Binding {`,' Binding}] `)' */ def funParams(mods: Modifiers, location: Location): List[Tree] = if in.token == LPAREN then @@ -3173,7 +3171,7 @@ object Parsers { * | AccessModifier * | override * | opaque - * LocalModifier ::= abstract | final | sealed | open | implicit | lazy | erased | inline | transparent + * LocalModifier ::= abstract | final | sealed | open | implicit | lazy | inline | transparent | infix | erased */ def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = { @tailrec diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index 3d7b81a606ab..44d5caba631a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -9,14 +9,13 @@ import ast.* import Names.Name import Phases.Phase import DenotTransformers.{DenotTransformer, IdentityDenotTransformer, SymTransformer} -import NamerOps.{methodType, linkConstructorParams} +import NamerOps.linkConstructorParams import NullOpsDecorator.stripNull import typer.ErrorReporting.err import typer.ProtoTypes.* import typer.TypeAssigner.seqLitType import typer.ConstFold import typer.ErrorReporting.{Addenda, NothingToAdd} -import NamerOps.methodType import config.Printers.recheckr import util.Property import StdNames.nme diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index ede6144afca3..06d35dfe4a4a 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -186,7 +186,7 @@ FunTypeArgs ::= InfixType | ‘(’ [ FunArgTypes ] ‘)’ | FunParamClause FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ -TypedFunParam ::= [`erased`] id ‘:’ IntoType +TypedFunParam ::= [`erased`] id ‘:’ Type MatchType ::= InfixType `match` <<< TypeCaseClauses >>> InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2) RefinedType ::= AnnotType {[nl] Refinement} RefinedTypeTree(t, ds) @@ -206,15 +206,11 @@ SimpleType1 ::= id Singleton ::= SimpleRef | SimpleLiteral | Singleton ‘.’ id -FunArgType ::= IntoType - | ‘=>’ IntoType PrefixOp(=>, t) +FunArgType ::= Type + | ‘=>’ Type PrefixOp(=>, t) FunArgTypes ::= FunArgType { ‘,’ FunArgType } ParamType ::= [‘=>’] ParamValueType ParamValueType ::= IntoType [‘*’] PostfixOp(t, "*") -IntoType ::= [‘into’] IntoTargetType Into(t) - | ‘(’ ‘into’ IntoTargetType ‘)’ -IntoTargetType ::= Type - | FunTypeArgs (‘=>’ | ‘?=>’) IntoType TypeArgs ::= ‘[’ Types ‘]’ ts Refinement ::= :<<< [RefineDef] {semi [RefineDcl]} >>> ds TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi) From 6159045fd1d6fe4d1c330ec8dccd6a21b3d2adf0 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 11 Feb 2024 21:40:49 +0100 Subject: [PATCH 3/5] Simplify erased parameter syntax erased is not always a modifier on the parameter name. The syntax `erased Type` is dropped. There's not really a need for this syntactic inconsistency. --- .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- tests/neg/lambda-infer.scala | 4 ++-- .../polymorphic-erased-functions-types.check | 20 +++++++++---------- .../polymorphic-erased-functions-types.scala | 4 ++-- tests/run/erased-lambdas.scala | 8 ++++---- tests/run/polymorphic-erased-functions.scala | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 13e30fc04890..fdd2f50af718 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1631,7 +1631,7 @@ object Parsers { () => funParam(in.offset, imods)) case t => def funArg() = - addErased() + erasedArgs.addOne(false) funArgType() commaSeparatedRest(t, funArg) accept(RPAREN) diff --git a/tests/neg/lambda-infer.scala b/tests/neg/lambda-infer.scala index 90f40aa05e86..6c3db90cb893 100644 --- a/tests/neg/lambda-infer.scala +++ b/tests/neg/lambda-infer.scala @@ -1,6 +1,6 @@ //> using options -language:experimental.erasedDefinitions -type F = (Int, erased Int) => Int +type F = (x: Int, erased y: Int) => Int erased class A @@ -14,7 +14,7 @@ erased class A use { (x, y) => x } // error: Expected F got (Int, Int) => Int - def singleParam(f: (erased Int) => Int) = f(5) + def singleParam(f: (erased x: Int) => Int) = f(5) singleParam(x => 5) // error: Expected (erased Int) => Int got Int => Int singleParam((erased x) => 5) // ok diff --git a/tests/neg/polymorphic-erased-functions-types.check b/tests/neg/polymorphic-erased-functions-types.check index 39d2720023cf..78a0ab37b1ab 100644 --- a/tests/neg/polymorphic-erased-functions-types.check +++ b/tests/neg/polymorphic-erased-functions-types.check @@ -5,11 +5,11 @@ | Required: [T] => (x$1: T) => Unit | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/polymorphic-erased-functions-types.scala:4:37 --------------------------------- -4 |def t1b: [T] => (erased T) => Unit = [T] => (t: T) => () // error - | ^^^^^^^^^^^^^^^^^^^ - | Found: [T] => (t: T) => Unit - | Required: [T] => (erased x$1: T) => Unit +-- [E007] Type Mismatch Error: tests/neg/polymorphic-erased-functions-types.scala:4:40 --------------------------------- +4 |def t1b: [T] => (erased t: T) => Unit = [T] => (t: T) => () // error + | ^^^^^^^^^^^^^^^^^^^ + | Found: [T] => (t: T) => Unit + | Required: [T] => (erased t: T) => Unit | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg/polymorphic-erased-functions-types.scala:6:36 --------------------------------- @@ -19,10 +19,10 @@ | Required: [T, U] => (x$1: T, x$2: U) => Unit | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/polymorphic-erased-functions-types.scala:7:43 --------------------------------- -7 |def t2b: [T, U] => (T, erased U) => Unit = [T, U] => (t: T, u: U) => () // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: [T, U] => (t: T, u: U) => Unit - | Required: [T, U] => (x$1: T, erased x$2: U) => Unit +-- [E007] Type Mismatch Error: tests/neg/polymorphic-erased-functions-types.scala:7:49 --------------------------------- +7 |def t2b: [T, U] => (t: T, erased u: U) => Unit = [T, U] => (t: T, u: U) => () // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: [T, U] => (t: T, u: U) => Unit + | Required: [T, U] => (t: T, erased u: U) => Unit | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/polymorphic-erased-functions-types.scala b/tests/neg/polymorphic-erased-functions-types.scala index d453c4602bad..c49a5c05c74e 100644 --- a/tests/neg/polymorphic-erased-functions-types.scala +++ b/tests/neg/polymorphic-erased-functions-types.scala @@ -1,7 +1,7 @@ import language.experimental.erasedDefinitions def t1a: [T] => T => Unit = [T] => (erased t: T) => () // error -def t1b: [T] => (erased T) => Unit = [T] => (t: T) => () // error +def t1b: [T] => (erased t: T) => Unit = [T] => (t: T) => () // error def t2a: [T, U] => (T, U) => Unit = [T, U] => (t: T, erased u: U) => () // error -def t2b: [T, U] => (T, erased U) => Unit = [T, U] => (t: T, u: U) => () // error +def t2b: [T, U] => (t: T, erased u: U) => Unit = [T, U] => (t: T, u: U) => () // error diff --git a/tests/run/erased-lambdas.scala b/tests/run/erased-lambdas.scala index 9c107e0fa0d4..e6be086bec8c 100644 --- a/tests/run/erased-lambdas.scala +++ b/tests/run/erased-lambdas.scala @@ -3,8 +3,8 @@ // lambdas should parse and work -type F = (erased Int, String) => String -type S = (Int, erased String) => Int +type F = (erased x: Int, y: String) => String +type S = (x: Int, erased y: String) => Int def useF(f: F) = f(5, "a") def useS(f: S) = f(5, "a") @@ -16,7 +16,7 @@ val fsExpl = (x: Int, erased y: String) => x // contextual lambdas should work -type FC = (Int, erased String) ?=> Int +type FC = (x: Int, erased y: String) ?=> Int def useCtx(f: FC) = f(using 5, "a") @@ -25,7 +25,7 @@ val fCvExpl = (x: Int, erased y: String) ?=> x // nested lambdas should work -val nested: Int => (String, erased Int) => FC = a => (_, erased _) => (c, erased d) ?=> a + c +val nested: Int => (x: String, erased y: Int) => FC = a => (_, erased _) => (c, erased d) ?=> a + c @main def Test() = assert("a" == useF(ff)) diff --git a/tests/run/polymorphic-erased-functions.scala b/tests/run/polymorphic-erased-functions.scala index 4086423d8c6a..40fd548a67af 100644 --- a/tests/run/polymorphic-erased-functions.scala +++ b/tests/run/polymorphic-erased-functions.scala @@ -3,8 +3,8 @@ import language.experimental.erasedDefinitions object Test extends App { // Types - type F1 = [T] => (erased T) => Int - type F2 = [T, U] => (T, erased U) => T + type F1 = [T] => (erased x: T) => Int + type F2 = [T, U] => (t: T, erased u: U) => T // Terms val t1 = [T] => (erased t: T) => 3 From 32723c5811a88a5ccad9c2ead16dcbb0610f80ab Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 13 Feb 2024 22:30:18 +0100 Subject: [PATCH 4/5] Address review comments --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- docs/_docs/internals/syntax.md | 9 ++++----- docs/_docs/reference/syntax.md | 11 +++++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index fdd2f50af718..fbb4e1319948 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1539,7 +1539,7 @@ object Parsers { def typ(): Tree = val start = in.offset var imods = Modifiers() - var erasedArgs: ListBuffer[Boolean] = ListBuffer() + val erasedArgs: ListBuffer[Boolean] = ListBuffer() def functionRest(params: List[Tree]): Tree = val paramSpan = Span(start, in.lastOffset) @@ -3324,7 +3324,7 @@ object Parsers { /** ClsTermParamClause ::= ‘(’ ClsParams ‘)’ | UsingClsTermParamClause * UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} - * ClsParam ::= {Annotation} + * ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’)] Param * * TypelessClause ::= DefTermParamClause * | UsingParamClause diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 06d35dfe4a4a..005ff174cd12 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -210,9 +210,9 @@ FunArgType ::= Type | ‘=>’ Type PrefixOp(=>, t) FunArgTypes ::= FunArgType { ‘,’ FunArgType } ParamType ::= [‘=>’] ParamValueType -ParamValueType ::= IntoType [‘*’] PostfixOp(t, "*") +ParamValueType ::= Type [‘*’] PostfixOp(t, "*") TypeArgs ::= ‘[’ Types ‘]’ ts -Refinement ::= :<<< [RefineDef] {semi [RefineDcl]} >>> ds +Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>> ds TypeBounds ::= [‘>:’ Type] [‘<:’ Type] TypeBoundsTree(lo, hi) TypeParamBounds ::= TypeBounds {‘:’ Type} ContextBounds(typeBounds, tps) Types ::= Type {‘,’ Type} @@ -299,7 +299,7 @@ BlockStat ::= Import | Expr1 | EndMarker TypeBlock ::= {TypeBlockStat semi} Type -TypeBlockStat ::= ‘type’ {nl} TypeDcl +TypeBlockStat ::= ‘type’ {nl} TypeDef ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr ForYield(enums, expr) / ForDo(enums, expr) | ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr @@ -359,7 +359,7 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var - [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param + [{Modifier} (‘val’ | ‘var’)] Param DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent DefParamClause ::= DefTypeParamClause @@ -473,7 +473,6 @@ TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> TemplateStat ::= Import | Export | {Annotation [nl]} {Modifier} Def - | {Annotation [nl]} {Modifier} Dcl | Extension | Expr1 | EndMarker diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index be51d1f3db1a..1980bc4e0ab2 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -294,7 +294,7 @@ BlockStat ::= Import | Expr1 | EndMarker TypeBlock ::= {TypeBlockStat semi} Type -TypeBlockStat ::= ‘type’ {nl} TypeDcl +TypeBlockStat ::= ‘type’ {nl} TypeDef ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr | ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr @@ -350,8 +350,12 @@ ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} -ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param +ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’)] Param +DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent +DefParamClause ::= DefTypeParamClause + | DefTermParamClause + | UsingParamClause TypelessClauses ::= TypelessClause {TypelessClause} TypelessClause ::= DefTermParamClause | UsingParamClause @@ -418,7 +422,7 @@ DefDcl ::= DefSig ‘:’ Type Def ::= ‘val’ PatDef | ‘var’ PatDef | ‘def’ DefDef - | ‘type’ {nl} TypeDcl + | ‘type’ {nl} TypeDef | TmplDef PatDef ::= ids [‘:’ Type] [‘=’ Expr] | Pattern2 [‘:’ Type] [‘=’ Expr] PatDef(_, pats, tpe?, expr) @@ -458,7 +462,6 @@ TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> TemplateStat ::= Import | Export | {Annotation [nl]} {Modifier} Def - | {Annotation [nl]} {Modifier} Dcl | Extension | Expr1 | EndMarker From f2f505194687d2bfe2de6d9688ff79518360f12d Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 13 Feb 2024 22:59:07 +0100 Subject: [PATCH 5/5] Update docs/_docs/internals/syntax.md Co-authored-by: Eugene Flesselle --- docs/_docs/internals/syntax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 005ff174cd12..cfa77c6ff965 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -104,7 +104,7 @@ semi ::= ‘;’ | nl {nl} The principle of optional braces is that any keyword that can be followed by `{` can also be followed by an indented block, without needing an intervening `:`. (Allowing an optional `:` would be counterproductive since it would introduce several ways to do the same thing.) -The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). +The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](../reference/other-new-features/indentation.md). 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