From c2bab938cac618294acbf49f9ebe7df1a512e1f0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Apr 2019 12:00:07 +0200 Subject: [PATCH 1/7] Disallow refinements in GivenTypes Test case is i2567.scala. We would like to write ``` class Foo given T { ... } ``` But previously this mistook the class body as a refinement for `T`. --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 12 ++++++------ docs/docs/internals/syntax.md | 11 +++++------ tests/run/i2567.check | 6 ++++++ tests/run/i2567.scala | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 tests/run/i2567.check create mode 100644 tests/run/i2567.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 20944cb4aaa2..1279b17e77e7 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2082,12 +2082,12 @@ object Parsers { if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil /** ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’ - * | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | ContextTypes) + * | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes) * ClsParams ::= ClsParam {`' ClsParam} * ClsParam ::= {Annotation} [{ParamModifier} (`val' | `var') | `inline'] Param * DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause - * GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | ContextTypes) - * ContextTypes ::= RefinedType {`,' RefinedType} + * GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes) + * GivenTypes ::= RefinedType {`,' RefinedType} * DefParams ::= DefParam {`,' DefParam} * DefParam ::= {Annotation} [`inline'] Param * Param ::= id `:' ParamType [`=' Expr] @@ -2203,7 +2203,7 @@ object Parsers { params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length)) } else if (isContextual) { - val tps = commaSeparated(refinedType) + val tps = commaSeparated(() => annotType()) var counter = nparams def nextIdx = { counter += 1; counter } val params = tps.map(makeSyntheticParameter(nextIdx, _, Given | Implicit)) @@ -2612,8 +2612,8 @@ object Parsers { /** InstanceDef ::= [id] InstanceParams InstanceBody * InstanceParams ::= [DefTypeParamClause] {GivenParamClause} - * InstanceBody ::= [‘of’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] - * | ‘of’ Type ‘=’ Expr + * InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] + * | ‘for’ Type ‘=’ Expr */ def instanceDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) { var mods1 = addMod(mods, instanceMod) diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 6381d23ca996..7bc29e4782ed 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -292,7 +292,7 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] | ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’ - | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | ContextTypes) + | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes) ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param @@ -301,10 +301,10 @@ Param ::= id ‘:’ ParamType [‘=’ Expr] DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause -GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | ContextTypes) +GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes) DefParams ::= DefParam {‘,’ DefParam} DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. -ContextTypes ::= RefinedType {‘,’ RefinedType} +GivenTypes ::= AnnotType {‘,’ AnnotType} ClosureMods ::= { ‘implicit’ | ‘erased’ | ‘given’} ``` @@ -356,7 +356,6 @@ Def ::= ‘val’ PatDef | ‘var’ VarDef | ‘def’ DefDef | ‘type’ {nl} TypeDcl - | ‘instance’ {nl} InstanceDef | TmplDef | INT PatDef ::= Pattern2 {‘,’ Pattern2} [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr) @@ -379,8 +378,8 @@ ObjectDef ::= id [Template] EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template) InstanceDef ::= [id] InstanceParams InstanceBody InstanceParams ::= [DefTypeParamClause] {GivenParamClause} -InstanceBody ::= [‘of’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] - | ‘of’ Type ‘=’ Expr +InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] + | ‘for’ Type ‘=’ Expr Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {‘with’ ConstrApp} diff --git a/tests/run/i2567.check b/tests/run/i2567.check new file mode 100644 index 000000000000..3cac4ab61117 --- /dev/null +++ b/tests/run/i2567.check @@ -0,0 +1,6 @@ +hi +hi +hi +hi +hi +hi diff --git a/tests/run/i2567.scala b/tests/run/i2567.scala new file mode 100644 index 000000000000..aef27f2e0cd6 --- /dev/null +++ b/tests/run/i2567.scala @@ -0,0 +1,15 @@ +class TC +implied tc for TC + +class Foo given TC { + println("hi") +} + +object Test extends App { + new Foo + new Foo given tc + new Foo() + new Foo() given tc + Foo() + Foo() given tc +} \ No newline at end of file From 9b1842d0e8226edb2b671a63f4816971b333eb6b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Apr 2019 12:33:59 +0200 Subject: [PATCH 2/7] Update docs to new syntax terms --- docs/docs/reference/contextual/inferable-params.md | 8 ++++---- docs/docs/reference/contextual/instance-defs.md | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/docs/reference/contextual/inferable-params.md b/docs/docs/reference/contextual/inferable-params.md index 39f5229427ae..708cf9e0ce26 100644 --- a/docs/docs/reference/contextual/inferable-params.md +++ b/docs/docs/reference/contextual/inferable-params.md @@ -101,11 +101,11 @@ Functions like `the` that have only inferable parameters are also called _contex Here is the new syntax of parameters and arguments seen as a delta from the [standard context free syntax of Scala 3](http://dotty.epfl.ch/docs/internals/syntax.html). ``` ClsParamClause ::= ... - | ‘given’ (‘(’ [ClsParams] ‘)’ | ContextTypes) + | ‘given’ (‘(’ [ClsParams] ‘)’ | GivenTypes) DefParamClause ::= ... - | InferParamClause -InferParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | ContextTypes) -ContextTypes ::= RefinedType {‘,’ RefinedType} + | GivenParamClause +GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes) +GivenTypes ::= RefinedType {‘,’ RefinedType} InfixExpr ::= ... | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs) diff --git a/docs/docs/reference/contextual/instance-defs.md b/docs/docs/reference/contextual/instance-defs.md index d985f31a928f..de60388eb3a6 100644 --- a/docs/docs/reference/contextual/instance-defs.md +++ b/docs/docs/reference/contextual/instance-defs.md @@ -67,11 +67,11 @@ Here is the new syntax of implied instance definitions, seen as a delta from the TmplDef ::= ... | ‘implied’ InstanceDef InstanceDef ::= [id] InstanceParams InstanceBody -InstanceParams ::= [DefTypeParamClause] {InferParamClause} -InferParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | ContextTypes) +InstanceParams ::= [DefTypeParamClause] {GivenParamClause} +InferParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes) InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] | ‘for’ Type ‘=’ Expr -ContextTypes ::= RefinedType {‘,’ RefinedType} +GivenTypes ::= RefinedType {‘,’ RefinedType} ``` The identifier `id` can be omitted only if either the `for` part or the template body is present. If the `for` part is missing, the template body must define at least one extension method. From 62afff8294cc1ffc854d61f4701e2b32c3ee82f7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Apr 2019 13:28:57 +0200 Subject: [PATCH 3/7] Update fromTasty blacklist I believe there might be something wrong with how given parameters are treated in tasty reflect. --- compiler/test/dotc/run-from-tasty.blacklist | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index fe0acf63becb..10b7644a5d93 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -3,3 +3,6 @@ eff-dependent.scala # It seems we still harmonize types in fromTasty. Not sure where this happens puzzle.scala + +# We get: class Foo needs to be abstract, since implicit val x$1: TC is not defined +i2567.scala From 04d309ab842100865bfb82923206bdb33f8295c0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Apr 2019 16:57:59 +0200 Subject: [PATCH 4/7] Allow to use `(...)` around GivenTypes --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 14 +++++++++++++- compiler/src/dotty/tools/dotc/parsing/Tokens.scala | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 1279b17e77e7..8985088ff2fb 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2190,7 +2190,19 @@ object Parsers { } val isContextual = initialMods.is(Given) newLineOptWhenFollowedBy(LPAREN) - if (in.token == LPAREN) { + def isParamClause: Boolean = + !isContextual || { + val lookahead = in.lookaheadScanner + lookahead.nextToken() + paramIntroTokens.contains(lookahead.token) && { + lookahead.token != IDENTIFIER || + lookahead.name == nme.inline || { + lookahead.nextToken() + lookahead.token == COLON + } + } + } + if (in.token == LPAREN && isParamClause) { if (ofInstance && !isContextual) syntaxError(em"parameters of instance definitions must come after `given'") val params = paramClause( diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 56b5e8b825ac..6269e6608db5 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -240,6 +240,8 @@ object Tokens extends TokensCommon { final val modifierFollowers = modifierTokens | defIntroTokens + final val paramIntroTokens: TokenSet = modifierTokens | identifierTokens | BitSet(AT, VAL, VAR, IMPLICIT) + /** Is token only legal as start of statement (eof also included)? */ final val mustStartStatTokens: TokenSet = defIntroTokens | modifierTokens | BitSet(IMPORT, EXPORT, PACKAGE) From c460b8c12c1c1421db9f01b7eef16a8379193f43 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Apr 2019 17:00:03 +0200 Subject: [PATCH 5/7] Update docs --- docs/docs/reference/contextual/inferable-params.md | 5 ++--- docs/docs/reference/contextual/instance-defs.md | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/docs/reference/contextual/inferable-params.md b/docs/docs/reference/contextual/inferable-params.md index 708cf9e0ce26..a3d2b1e80fdc 100644 --- a/docs/docs/reference/contextual/inferable-params.md +++ b/docs/docs/reference/contextual/inferable-params.md @@ -41,8 +41,7 @@ def maximum[T](xs: List[T]) given Ord[T]: T = inferred argument to `max`. The name of the parameter is left out. Generally, inferable parameters may be given either as a parameter list `(p_1: T_1, ..., p_n: T_n)` -or as a sequence of types, separated by commas. To distinguish the two, a leading -`(` always indicates a parameter list. +or as a sequence of types, separated by commas. ## Inferring Complex Arguments @@ -105,7 +104,7 @@ ClsParamClause ::= ... DefParamClause ::= ... | GivenParamClause GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes) -GivenTypes ::= RefinedType {‘,’ RefinedType} +GivenTypes ::= AnnotType {‘,’ AnnotType} InfixExpr ::= ... | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs) diff --git a/docs/docs/reference/contextual/instance-defs.md b/docs/docs/reference/contextual/instance-defs.md index de60388eb3a6..79fddb295b52 100644 --- a/docs/docs/reference/contextual/instance-defs.md +++ b/docs/docs/reference/contextual/instance-defs.md @@ -68,10 +68,10 @@ TmplDef ::= ... | ‘implied’ InstanceDef InstanceDef ::= [id] InstanceParams InstanceBody InstanceParams ::= [DefTypeParamClause] {GivenParamClause} -InferParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes) +GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes) InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody] | ‘for’ Type ‘=’ Expr -GivenTypes ::= RefinedType {‘,’ RefinedType} +GivenTypes ::= AnnotType {‘,’ AnnotType} ``` The identifier `id` can be omitted only if either the `for` part or the template body is present. If the `for` part is missing, the template body must define at least one extension method. From 5e019785a4d06909eb2f9b20c82856945e89b103 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Apr 2019 17:00:35 +0200 Subject: [PATCH 6/7] Don't copy implicit flags from getter to setter --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- tests/pos-special/fatal-warnings/i6290.scala | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/pos-special/fatal-warnings/i6290.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 8a4fec35b893..e6633af124c5 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -158,7 +158,7 @@ object desugar { vparamss = (setterParam :: Nil) :: Nil, tpt = TypeTree(defn.UnitType), rhs = setterRhs - ).withMods((mods | Accessor) &~ CaseAccessor) + ).withMods((mods | Accessor) &~ (CaseAccessor | Implicit | Given)) Thicket(vdef, setter) } else vdef diff --git a/tests/pos-special/fatal-warnings/i6290.scala b/tests/pos-special/fatal-warnings/i6290.scala new file mode 100644 index 000000000000..50cabb06ac1e --- /dev/null +++ b/tests/pos-special/fatal-warnings/i6290.scala @@ -0,0 +1,15 @@ +class TC { type T } + +class C given (TC { type T = Int }) + +def f1 given (x: TC) = ??? +def f2 given (@unchecked x: TC) = ??? +inline def f3 given (inline x: TC) = ??? + +class C1 given (x: TC) +class C2 given (@unchecked x: TC) +class C3 given (val x: TC) +class C4 given (var x: TC) +class C5 given (private val x: TC) +class C6 given (private[this] val x: TC) + From 48e515f405b3e05f0f2b52365f5f7f8f0d6ffe57 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Apr 2019 17:06:56 +0200 Subject: [PATCH 7/7] Also don't copy Lazy to Setter. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e6633af124c5..c3682329cd68 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -158,7 +158,7 @@ object desugar { vparamss = (setterParam :: Nil) :: Nil, tpt = TypeTree(defn.UnitType), rhs = setterRhs - ).withMods((mods | Accessor) &~ (CaseAccessor | Implicit | Given)) + ).withMods((mods | Accessor) &~ (CaseAccessor | Implicit | Given | Lazy)) Thicket(vdef, setter) } else vdef