Skip to content

Commit 13ac0c4

Browse files
committed
Make given clauses come last
We now forbid given clauses followed by normal parameter clauses. besides the syntactic awkwardness, there's also the problem of eta expansion. Example: ``` trait Universe { type T } def f given (u: Universe) (x: u.T) ``` How should we tea expand `f`? The usual algorithm would give: ``` (x: u.T) => (f given the[Universe])(x) ``` but that's ill typed, since `u` is not defined on the outside.
1 parent 659e1f1 commit 13ac0c4

File tree

4 files changed

+57
-45
lines changed

4 files changed

+57
-45
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,15 +2114,16 @@ object Parsers {
21142114
def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] =
21152115
if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil
21162116

2117-
/** ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’
2118-
* | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes)
2119-
* ClsParams ::= ClsParam {`' ClsParam}
2120-
* ClsParam ::= {Annotation} [{ParamModifier} (`val' | `var') | `inline'] Param
2121-
* DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause
2117+
/** ClsParamClause ::= [‘erased’] (‘(’ ClsParams ‘)’
2118+
* GivenClsParamClause::= ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes)
2119+
* ClsParams ::= ClsParam {‘,’ ClsParam}
2120+
* ClsParam ::= {Annotation}
2121+
*
2122+
* DefParamClause ::= [‘erased’] (‘(’ DefParams ‘)’
21222123
* GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes)
2123-
* GivenTypes ::= RefinedType {`,' RefinedType}
2124-
* DefParams ::= DefParam {`,' DefParam}
2125-
* DefParam ::= {Annotation} [`inline'] Param
2124+
* DefParams ::= DefParam {‘,’ DefParam}
2125+
* DefParam ::= {Annotation} [‘inline’] Param
2126+
*
21262127
* Param ::= id `:' ParamType [`=' Expr]
21272128
*
21282129
* @return the list of parameter definitions
@@ -2204,14 +2205,16 @@ object Parsers {
22042205
}
22052206

22062207
/** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
2208+
* | {ClsParamClause} {GivenClsParamClause}
22072209
* DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
2210+
* | {DefParamClause} {GivenParamClause}
22082211
*
22092212
* @return The parameter definitions
22102213
*/
22112214
def paramClauses(ofClass: Boolean = false,
22122215
ofCaseClass: Boolean = false,
22132216
ofInstance: Boolean = false): List[List[ValDef]] = {
2214-
def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] = {
2217+
def recur(firstClause: Boolean, nparams: Int, contextualOnly: Boolean): List[List[ValDef]] = {
22152218
var initialMods = EmptyModifiers
22162219
if (in.token == GIVEN) {
22172220
in.nextToken()
@@ -2236,28 +2239,29 @@ object Parsers {
22362239
}
22372240
}
22382241
if (in.token == LPAREN && isParamClause) {
2239-
if (ofInstance && !isContextual)
2240-
syntaxError(em"parameters of instance definitions must come after `given'")
2242+
if (contextualOnly && !isContextual)
2243+
if (ofInstance) syntaxError(em"parameters of instance definitions must come after `given'")
2244+
else syntaxError(em"normal parameters cannot come after `given' clauses")
22412245
val params = paramClause(
22422246
ofClass = ofClass,
22432247
ofCaseClass = ofCaseClass,
22442248
firstClause = firstClause,
22452249
initialMods = initialMods)
22462250
val lastClause =
22472251
params.nonEmpty && params.head.mods.flags.is(Implicit, butNot = Given)
2248-
params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length))
2252+
params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isContextual))
22492253
}
22502254
else if (isContextual) {
22512255
val tps = commaSeparated(() => annotType())
22522256
var counter = nparams
22532257
def nextIdx = { counter += 1; counter }
22542258
val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param
22552259
val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given | Implicit))
2256-
params :: recur(firstClause = false, nparams + params.length)
2260+
params :: recur(firstClause = false, nparams + params.length, isContextual)
22572261
}
22582262
else Nil
22592263
}
2260-
recur(firstClause = true, 0)
2264+
recur(firstClause = true, 0, ofInstance)
22612265
}
22622266

22632267
/* -------- DEFS ------------------------------------------- */

docs/docs/internals/syntax.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,18 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] |
292292
SubtypeBounds
293293
294294
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
295-
ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’
296-
| ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes)
295+
| {ClsParamClause} {GivenClsParamClause}
296+
ClsParamClause ::= [‘erased’] (‘(’ ClsParams ‘)’
297+
GivenClsParamClause::= ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes)
297298
ClsParams ::= ClsParam {‘,’ ClsParam}
298299
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
299300
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
300301
Param ::= id ‘:’ ParamType [‘=’ Expr]
301302
| INT
302303
303304
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
304-
DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause
305+
| {DefParamClause} {GivenParamClause}
306+
DefParamClause ::= [‘erased’] (‘(’ DefParams ‘)’
305307
GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes)
306308
DefParams ::= DefParam {‘,’ DefParam}
307309
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.

docs/docs/reference/contextual/inferable-params.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,24 +64,25 @@ maximum(xs) given (descending given ListOrd)
6464
maximum(xs) given (descending given (ListOrd given IntOrd))
6565
```
6666

67-
## Mixing Given Clauses And Normal Parameters
67+
## Multiple Given Clauses
6868

69-
Given clauses can be freely mixed with normal parameters.
70-
A given clause may be followed by a normal parameter and _vice versa_.
7169
There can be several given clauses in a definition. Example:
7270
```scala
73-
def f given (u: Universe) (x: u.T) given Context = ...
74-
75-
implied global for Universe { type T = String ... }
76-
implied ctx for Context { ... }
71+
def f given (u: Universe) given (x: u.Context) = ...
72+
```
73+
However, all `given` clauses in a definition must come after any normal parameter clauses.
74+
Multiple given clauses are matched left-to-right in applications. Example:
75+
```scala
76+
implied global for Universe { type Context = ... }
77+
implied ctx for global.Context { ... }
7778
```
7879
Then the following calls are all valid (and normalize to the last one)
7980
```scala
80-
f("abc")
81-
(f given global)("abc")
82-
f("abc") given ctx
83-
(f given global)("abc") given ctx
81+
f
82+
(f given global)
83+
(f given global) given ctx
8484
```
85+
But `f given ctx` would give a type error.
8586

8687
## Summoning Implied Instances
8788

@@ -100,13 +101,14 @@ Functions like `the` that have only context parameters are also called _context
100101

101102
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).
102103
```
103-
ClsParamClause ::= ...
104-
| ‘given’ (‘(’ [ClsParams] ‘)’ | GivenTypes)
105-
DefParamClause ::= ...
106-
| GivenParamClause
107-
GivenParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | GivenTypes)
108-
GivenTypes ::= AnnotType {‘,’ AnnotType}
109-
110-
InfixExpr ::= ...
111-
| InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
104+
ClsParamClauses ::= ...
105+
| {ClsParamClause} {GivenClsParamClause}
106+
GivenClsParamClause ::= ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | GivenTypes)
107+
DefParamClauses ::= ...
108+
| {DefParamClause} {GivenParamClause}
109+
GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | GivenTypes)
110+
GivenTypes ::= AnnotType {‘,’ AnnotType}
111+
112+
InfixExpr ::= ...
113+
| InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
112114
```

tests/neg/implicit-params.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
object Test {
22

33
case class C(x: Int)
4+
case class D(x: Int)
45

56
def f(x: Int) given (c: C) = x + c.x
67

7-
def g(x: Int) given (c: C) (y: Int) = x + c.x + y
8+
def g0(x: Int) given (c: C) (y: Int) = x + c.x + y // error
9+
10+
def g(x: Int) given (c: C) given D = x + c.x + the[D].x // OK
811

912
def h(x: Int) given () = x // error
1013

11-
implicit object C extends C(11)
14+
implied C for C(11)
15+
implied D for D(11)
1216

1317
f(1)
1418
f(1) given C
1519
f given 2 // error
1620
f(1)(C) // error
1721

18-
g(1)(2) // OK
19-
(g(1) given C)(2) // OK
20-
g(1) given 2 // error
21-
g(1) given C given 2 // error
22-
g(1)(C)(2) // error
23-
g(1)(C) given 2 // error
22+
g(1) // OK
23+
g(1) given C // OK
24+
g(1) given C given D(0) // OK
25+
g(1) given D // error
26+
g(1)(D) // error
27+
g(1)(C)(D) // error
2428
}

0 commit comments

Comments
 (0)