From fd2c24c3159cefa583889a176f31d1e2325fe7e6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 3 Dec 2016 12:53:10 +0100 Subject: Add syntax for implicit functions --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'compiler/src/dotty/tools/dotc/parsing/Parsers.scala') diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 704f399ca..dabd8d2b0 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -681,7 +681,7 @@ object Parsers { } } - /** Type ::= FunArgTypes `=>' Type + /** Type ::= [`implicit'] FunArgTypes `=>' Type * | HkTypeParamClause `->' Type * | InfixType * FunArgTypes ::= InfixType @@ -689,20 +689,26 @@ object Parsers { */ def typ(): Tree = { val start = in.offset + val isImplicit = in.token == IMPLICIT + if (isImplicit) in.nextToken() + def functionRest(params: List[Tree]): Tree = + atPos(start, accept(ARROW)) { + val t = typ() + if (isImplicit) new ImplicitFunction(params, t) else Function(params, t) + } val t = if (in.token == LPAREN) { in.nextToken() if (in.token == RPAREN) { in.nextToken() - atPos(start, accept(ARROW)) { Function(Nil, typ()) } + functionRest(Nil) } else { openParens.change(LPAREN, 1) val ts = commaSeparated(funArgType) openParens.change(LPAREN, -1) accept(RPAREN) - if (in.token == ARROW) - atPos(start, in.skipToken()) { Function(ts, typ()) } + if (isImplicit || in.token == ARROW) functionRest(ts) else { for (t <- ts) if (t.isInstanceOf[ByNameTypeTree]) @@ -722,7 +728,7 @@ object Parsers { else infixType() in.token match { - case ARROW => atPos(start, in.skipToken()) { Function(List(t), typ()) } + case ARROW => functionRest(t :: Nil) case FORSOME => syntaxError("existential types no longer supported; use a wildcard type or dependent type instead"); t case _ => t } -- cgit v1.2.3 From 63ba924a5ad9c01768be22f0b115c9501aa9f23a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 4 Dec 2016 12:44:08 +0100 Subject: Cleanup of implicit modifiers scheme Implicit modifiers were quite irregular compared to the other ones. This commit does a cleanup. --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- compiler/src/dotty/tools/dotc/core/Comments.scala | 2 +- .../src/dotty/tools/dotc/parsing/Parsers.scala | 25 +++++++++++----------- .../tools/dotc/parsing/ModifiersParsingTest.scala | 6 +++--- 4 files changed, 17 insertions(+), 18 deletions(-) (limited to 'compiler/src/dotty/tools/dotc/parsing/Parsers.scala') diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 25b69b1f5..9707770d5 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -115,7 +115,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Var() extends Mod(Flags.Mutable) - case class Implicit(flag: FlagSet = Flags.ImplicitCommon) extends Mod(flag) + case class Implicit() extends Mod(Flags.ImplicitCommon) case class Final() extends Mod(Flags.Final) diff --git a/compiler/src/dotty/tools/dotc/core/Comments.scala b/compiler/src/dotty/tools/dotc/core/Comments.scala index 1e623db4d..2559209c3 100644 --- a/compiler/src/dotty/tools/dotc/core/Comments.scala +++ b/compiler/src/dotty/tools/dotc/core/Comments.scala @@ -119,7 +119,7 @@ object Comments { def apply(comment: Comment, code: String, codePos: Position)(implicit ctx: Context) = new UseCase(comment, code, codePos) { val untpdCode = { - val tree = new Parser(new SourceFile("", code)).localDef(codePos.start, EmptyFlags) + val tree = new Parser(new SourceFile("", code)).localDef(codePos.start) tree match { case tree: untpd.DefDef => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index dabd8d2b0..5dd0df022 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1067,7 +1067,7 @@ object Parsers { case FOR => forExpr() case IMPLICIT => - implicitClosure(in.skipToken(), location) + implicitClosure(in.offset, location, atPos(in.skipToken()) { Mod.Implicit() }) case _ => expr1Rest(postfixExpr(), location) } @@ -1118,9 +1118,8 @@ object Parsers { /** Expr ::= implicit Id `=>' Expr * BlockResult ::= implicit Id [`:' InfixType] `=>' Block */ - def implicitClosure(start: Int, location: Location.Value, implicitMod: Option[Mod] = None): Tree = { - var mods = atPos(start) { Modifiers(Implicit) } - if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get) + def implicitClosure(start: Int, location: Location.Value, implicitMod: Mod): Tree = { + val mods = Modifiers(Implicit).withAddedMod(implicitMod) val id = termIdent() val paramExpr = if (location == Location.InBlock && in.token == COLON) @@ -1489,7 +1488,7 @@ object Parsers { private def modOfToken(tok: Int): Mod = tok match { case ABSTRACT => Mod.Abstract() case FINAL => Mod.Final() - case IMPLICIT => Mod.Implicit(ImplicitCommon) + case IMPLICIT => Mod.Implicit() case INLINE => Mod.Inline() case LAZY => Mod.Lazy() case OVERRIDE => Mod.Override() @@ -1745,7 +1744,7 @@ object Parsers { else { if (in.token == IMPLICIT) { implicitOffset = in.offset - implicitMod = atPos(in.skipToken()) { Mod.Implicit(Implicit) } + implicitMod = atPos(in.skipToken()) { Mod.Implicit() } } commaSeparated(param) } @@ -2218,9 +2217,9 @@ object Parsers { stats.toList } - def localDef(start: Int, implicitFlag: FlagSet, implicitMod: Option[Mod] = None): Tree = { - var mods = addFlag(defAnnotsMods(localModifierTokens), implicitFlag) - if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get) + def localDef(start: Int, implicitMod: Option[Mod] = None): Tree = { + var mods = defAnnotsMods(localModifierTokens) + for (imod <- implicitMod) mods = (mods | ImplicitCommon).withAddedMod(imod) defOrDcl(start, mods) } @@ -2243,11 +2242,11 @@ object Parsers { else if (isDefIntro(localModifierTokens)) if (in.token == IMPLICIT) { val start = in.offset - val mod = atPos(in.skipToken()) { Mod.Implicit(ImplicitCommon) } - if (isIdent) stats += implicitClosure(start, Location.InBlock, Some(mod)) - else stats += localDef(start, ImplicitCommon, Some(mod)) + val mod = atPos(in.skipToken()) { Mod.Implicit() } + if (isIdent) stats += implicitClosure(start, Location.InBlock, mod) + else stats += localDef(start, Some(mod)) } else { - stats += localDef(in.offset, EmptyFlags) + stats += localDef(in.offset) } else if (!isStatSep && (in.token != CASE)) { exitOnError = mustStartStat diff --git a/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala b/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala index e31ef2160..32f842e92 100644 --- a/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/ModifiersParsingTest.scala @@ -140,12 +140,12 @@ class ModifiersParsingTest { source = "def f(implicit a: Int, b: Int) = ???" println(source.defParam(0).modifiers) - assert(source.defParam(0).modifiers == List(Mod.Implicit(Flags.Implicit))) - assert(source.defParam(1).modifiers == List(Mod.Implicit(Flags.Implicit))) + assert(source.defParam(0).modifiers == List(Mod.Implicit())) + assert(source.defParam(1).modifiers == List(Mod.Implicit())) source = "def f(x: Int, y: Int)(implicit a: Int, b: Int) = ???" assert(source.defParam(0, 0).modifiers == List()) - assert(source.defParam(1, 0).modifiers == List(Mod.Implicit(Flags.Implicit))) + assert(source.defParam(1, 0).modifiers == List(Mod.Implicit())) } @Test def blockDef = { -- cgit v1.2.3 From ee59c23e9033ed775c64583c849bd47cc3f195af Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 4 Dec 2016 14:41:28 +0100 Subject: Generalize syntax for implicit function values - allow more than one implicit binding - harmonize syntax in expressions and blocks --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 117 +++++++++++++-------- compiler/src/dotty/tools/dotc/parsing/Tokens.scala | 2 + docs/syntax-summary.txt | 12 +-- 3 files changed, 81 insertions(+), 50 deletions(-) (limited to 'compiler/src/dotty/tools/dotc/parsing/Parsers.scala') diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 5dd0df022..6fb10fd39 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -146,6 +146,7 @@ object Parsers { def isNumericLit = numericLitTokens contains in.token def isModifier = modifierTokens contains in.token def isExprIntro = canStartExpressionTokens contains in.token + def isBindingIntro = canStartBindingTokens contains in.token def isTemplateIntro = templateIntroTokens contains in.token def isDclIntro = dclIntroTokens contains in.token def isStatSeqEnd = in.token == RBRACE || in.token == EOF @@ -951,14 +952,14 @@ object Parsers { } } - /** Expr ::= FunParams `=>' Expr + /** Expr ::= [`implicit'] FunParams `=>' Expr * | Expr1 * FunParams ::= Bindings - * | [`implicit'] Id + * | Id * | `_' * ExprInParens ::= PostfixExpr `:' Type * | Expr - * BlockResult ::= (FunParams | [`implicit'] Id `:' InfixType) => Block + * BlockResult ::= [`implicit'] FunParams `=>' Block * | Expr1 * Expr1 ::= `if' `(' Expr `)' {nl} Expr [[semi] else Expr] * | `if' Expr `then' Expr [[semi] else Expr] @@ -985,22 +986,27 @@ object Parsers { def expr(): Tree = expr(Location.ElseWhere) def expr(location: Location.Value): Tree = { - val saved = placeholderParams - placeholderParams = Nil - val t = expr1(location) - if (in.token == ARROW) { - placeholderParams = saved - closureRest(startOffset(t), location, convertToParams(t)) - } - else if (isWildcard(t)) { - placeholderParams = placeholderParams ::: saved - t + val start = in.offset + if (in.token == IMPLICIT) + implicitClosure(start, location, implicitMods()) + else { + val saved = placeholderParams + placeholderParams = Nil + val t = expr1(location) + if (in.token == ARROW) { + placeholderParams = saved + closureRest(start, location, convertToParams(t)) + } + else if (isWildcard(t)) { + placeholderParams = placeholderParams ::: saved + t + } + else + try + if (placeholderParams.isEmpty) t + else new WildcardFunction(placeholderParams.reverse, t) + finally placeholderParams = saved } - else - try - if (placeholderParams.isEmpty) t - else new WildcardFunction(placeholderParams.reverse, t) - finally placeholderParams = saved } def expr1(location: Location.Value = Location.ElseWhere): Tree = in.token match { @@ -1066,8 +1072,6 @@ object Parsers { atPos(in.skipToken()) { Return(if (isExprIntro) expr() else EmptyTree, EmptyTree) } case FOR => forExpr() - case IMPLICIT => - implicitClosure(in.offset, location, atPos(in.skipToken()) { Mod.Implicit() }) case _ => expr1Rest(postfixExpr(), location) } @@ -1115,19 +1119,43 @@ object Parsers { } } + /** FunParams ::= Bindings + * | Id + * | `_' + * Bindings ::= `(' [Binding {`,' Binding}] `)' + */ + def funParams(mods: Modifiers, location: Location.Value): List[Tree] = + if (in.token == LPAREN) + inParens(if (in.token == RPAREN) Nil else commaSeparated(() => binding(mods))) + else { + val start = in.offset + val name = bindingName() + val t = + if (in.token == COLON && location == Location.InBlock) { + in.nextToken() + infixType() + } + else TypeTree() + (atPos(start) { makeParameter(name, t, mods) }) :: Nil + } + + /** Binding ::= (Id | `_') [`:' Type] + */ + def binding(mods: Modifiers): Tree = + atPos(in.offset) { makeParameter(bindingName(), typedOpt(), mods) } + + def bindingName(): TermName = + if (in.token == USCORE) { + in.nextToken() + ctx.freshName(nme.USCORE_PARAM_PREFIX).toTermName + } + else ident() + /** Expr ::= implicit Id `=>' Expr - * BlockResult ::= implicit Id [`:' InfixType] `=>' Block - */ - def implicitClosure(start: Int, location: Location.Value, implicitMod: Mod): Tree = { - val mods = Modifiers(Implicit).withAddedMod(implicitMod) - val id = termIdent() - val paramExpr = - if (location == Location.InBlock && in.token == COLON) - atPos(startOffset(id), in.skipToken()) { Typed(id, infixType()) } - else - id - closureRest(start, location, convertToParam(paramExpr, mods) :: Nil) - } + * BlockResult ::= implicit Id [`:' InfixType] `=>' Block // Scala2 only + */ + def implicitClosure(start: Int, location: Location.Value, implicitMods: Modifiers): Tree = + closureRest(start, location, funParams(implicitMods, location)) def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree = atPos(start, in.offset) { @@ -1581,6 +1609,9 @@ object Parsers { normalize(loop(start)) } + def implicitMods(): Modifiers = + addMod(EmptyModifiers, atPos(accept(IMPLICIT)) { Mod.Implicit() }) + /** Wrap annotation or constructor in New(...). */ def wrapNew(tpt: Tree) = Select(New(tpt), nme.CONSTRUCTOR) @@ -1686,9 +1717,9 @@ object Parsers { * Param ::= id `:' ParamType [`=' Expr] */ def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = { - var implicitMod: Mod = null - var firstClauseOfCaseClass = ofCaseClass + var imods: Modifiers = EmptyModifiers var implicitOffset = -1 // use once + var firstClauseOfCaseClass = ofCaseClass def param(): ValDef = { val start = in.offset var mods = annotsAsMods() @@ -1723,7 +1754,7 @@ object Parsers { if (in.token == ARROW) { if (owner.isTypeName && !(mods is Local)) syntaxError(s"${if (mods is Mutable) "`var'" else "`val'"} parameters may not be call-by-name") - else if (implicitMod != null) + else if (imods.hasFlags) syntaxError("implicit parameters may not be call-by-name") } paramType() @@ -1735,7 +1766,7 @@ object Parsers { mods = mods.withPos(mods.pos.union(Position(implicitOffset, implicitOffset))) implicitOffset = -1 } - if (implicitMod != null) mods = addMod(mods, implicitMod) + for (imod <- imods.mods) mods = addMod(mods, imod) ValDef(name, tpt, default).withMods(mods) } } @@ -1744,7 +1775,7 @@ object Parsers { else { if (in.token == IMPLICIT) { implicitOffset = in.offset - implicitMod = atPos(in.skipToken()) { Mod.Implicit() } + imods = implicitMods() } commaSeparated(param) } @@ -1754,7 +1785,7 @@ object Parsers { if (in.token == LPAREN) paramClause() :: { firstClauseOfCaseClass = false - if (implicitMod == null) clauses() else Nil + if (imods.hasFlags) Nil else clauses() } else Nil } @@ -2217,9 +2248,9 @@ object Parsers { stats.toList } - def localDef(start: Int, implicitMod: Option[Mod] = None): Tree = { + def localDef(start: Int, implicitMods: Modifiers = EmptyModifiers): Tree = { var mods = defAnnotsMods(localModifierTokens) - for (imod <- implicitMod) mods = (mods | ImplicitCommon).withAddedMod(imod) + for (imod <- implicitMods.mods) mods = addMod(mods, imod) defOrDcl(start, mods) } @@ -2242,9 +2273,9 @@ object Parsers { else if (isDefIntro(localModifierTokens)) if (in.token == IMPLICIT) { val start = in.offset - val mod = atPos(in.skipToken()) { Mod.Implicit() } - if (isIdent) stats += implicitClosure(start, Location.InBlock, mod) - else stats += localDef(start, Some(mod)) + val imods = implicitMods() + if (isBindingIntro) stats += implicitClosure(start, Location.InBlock, imods) + else stats += localDef(start, imods) } else { stats += localDef(in.offset) } diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 5324207db..280832ef3 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -209,6 +209,8 @@ object Tokens extends TokensCommon { final val canStartTypeTokens = literalTokens | identifierTokens | BitSet( THIS, SUPER, USCORE, LPAREN, AT) + final val canStartBindingTokens = identifierTokens | BitSet(USCORE, LPAREN) + final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, CASECLASS, CASEOBJECT) final val dclIntroTokens = BitSet(DEF, VAL, VAR, TYPE) diff --git a/docs/syntax-summary.txt b/docs/syntax-summary.txt index fe0ebc89c..d382507d3 100644 --- a/docs/syntax-summary.txt +++ b/docs/syntax-summary.txt @@ -125,13 +125,13 @@ grammar. TypeBounds ::= [`>:' Type] [`<: Type] | INT TypeBoundsTree(lo, hi) TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type} ContextBounds(typeBounds, tps) - Expr ::= FunParams `=>' Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr) + Expr ::= [`implicit'] FunParams `=>' Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr) FunParams ::= Bindings - | [`implicit'] id + | id | `_' ExprInParens ::= PostfixExpr `:' Type | Expr - BlockResult ::= (FunParams | [`implicit'] id `:' InfixType) `=>' Block + BlockResult ::= [`implicit'] FunParams `=>' Block | Expr1 Expr1 ::= `if' `(' Expr `)' {nl} Expr [[semi] else Expr] If(Parens(cond), thenp, elsep?) | `if' Expr `then' Expr [[semi] else Expr] If(cond, thenp, elsep?) @@ -178,9 +178,7 @@ grammar. | {Annotation} {LocalModifier} TmplDef | Expr1 | - ResultExpr ::= Expr1 - | (Bindings | ([`implicit'] id | `_') `:' ) `=>' Block - Function(args, block) // block starts at => + ForExpr ::= `for' (`(' Enumerators `)' | `{' Enumerators `}') ForYield(enums, expr) {nl} [`yield'] Expr ForDo(enums, expr) | `for' Enumerators (`do' Expr | `yield' Expr) @@ -241,7 +239,7 @@ grammar. DefParams ::= DefParam {`,' DefParam} DefParam ::= {Annotation} [`inline'] Param ValDef(mods, id, tpe, expr) -- point of mods at id. - Bindings ::= `(' Binding {`,' Binding `)' bindings + Bindings ::= `(' Binding {`,' Binding}] `)' Binding ::= (id | `_') [`:' Type] ValDef(_, id, tpe, EmptyTree) Modifier ::= LocalModifier -- cgit v1.2.3 From aecfb37919291f3d191aa3c04f753cc2df4d962a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 4 Dec 2016 15:44:04 +0100 Subject: Add code to disable old implicit closure syntax in blocks This will no longer be supported. On the other hand, as long as the alternative is not yet legal in Scala2.x we cannot flag this as an error. So the migration warning/error and patch code is currently disabled. --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'compiler/src/dotty/tools/dotc/parsing/Parsers.scala') diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6fb10fd39..26656aae8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1132,8 +1132,18 @@ object Parsers { val name = bindingName() val t = if (in.token == COLON && location == Location.InBlock) { + if (false) // Don't error yet, as the alternative syntax "implicit (x: T) => ... " + // is not supported by Scala2.x + migrationWarningOrError(s"This syntax is no longer supported; parameter needs to be enclosed in (...)") + in.nextToken() - infixType() + val t = infixType() + + if (false && in.isScala2Mode) { + patch(source, Position(start), "(") + patch(source, Position(in.lastOffset), ")") + } + t } else TypeTree() (atPos(start) { makeParameter(name, t, mods) }) :: Nil -- cgit v1.2.3