Skip to content

Commit 4693a88

Browse files
authored
Merge pull request #6488 from dotty-staging/given-constr
Allow `given` in constructor applications
2 parents 12d3147 + 62e18d7 commit 4693a88

File tree

9 files changed

+96
-21
lines changed

9 files changed

+96
-21
lines changed

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,9 +424,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
424424
vdef.withMods(mods | Param)
425425
}
426426

427-
def makeSyntheticParameter(n: Int = 1, tpt: Tree = null, flags: FlagSet = EmptyFlags)(implicit ctx: Context): ValDef =
427+
def makeSyntheticParameter(n: Int = 1, tpt: Tree = null, flags: FlagSet = SyntheticTermParam)(implicit ctx: Context): ValDef =
428428
ValDef(nme.syntheticParamName(n), if (tpt == null) TypeTree() else tpt, EmptyTree)
429-
.withFlags(flags | SyntheticTermParam)
429+
.withFlags(flags)
430430

431431
def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context): Tree =
432432
if (tparams.isEmpty) tpt else LambdaTypeTree(tparams, tpt)

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

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -575,22 +575,26 @@ object Parsers {
575575
}
576576
else recur(operand())
577577
}
578-
else if (in.token == GIVEN) {
578+
else if (in.token == GIVEN && !isType) {
579579
val top1 = reduceStack(base, top, minInfixPrec, leftAssoc = true, nme.WITHkw, isType)
580580
assert(opStack `eq` base)
581-
val app = atSpan(startOffset(top1), in.offset) {
582-
in.nextToken()
583-
val args = if (in.token == LPAREN) parArgumentExprs() else operand() :: Nil
584-
Apply(top, args)
585-
}
586-
app.pushAttachment(ApplyGiven, ())
587-
recur(app)
581+
recur(applyGiven(top1, operand))
588582
}
589583
else reduceStack(base, top, minPrec, leftAssoc = true, in.name, isType)
590584

591585
recur(first)
592586
}
593587

588+
def applyGiven(t: Tree, operand: () => Tree): Tree = {
589+
val app = atSpan(startOffset(t), in.offset) {
590+
in.nextToken()
591+
val args = if (in.token == LPAREN) parArgumentExprs() else operand() :: Nil
592+
Apply(t, args)
593+
}
594+
app.pushAttachment(ApplyGiven, ())
595+
app
596+
}
597+
594598
/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */
595599

596600
/** Accept identifier and return its name as a term name. */
@@ -2266,7 +2270,8 @@ object Parsers {
22662270
val tps = commaSeparated(() => annotType())
22672271
var counter = nparams
22682272
def nextIdx = { counter += 1; counter }
2269-
val params = tps.map(makeSyntheticParameter(nextIdx, _, Given | Implicit))
2273+
val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param
2274+
val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given | Implicit))
22702275
params :: recur(firstClause = false, nparams + params.length)
22712276
}
22722277
else Nil
@@ -2724,13 +2729,43 @@ object Parsers {
27242729

27252730
/* -------- TEMPLATES ------------------------------------------- */
27262731

2727-
/** ConstrApp ::= SimpleType {ParArgumentExprs}
2732+
/** SimpleConstrApp ::= AnnotType {ParArgumentExprs}
2733+
* ConstrApp ::= SimpleConstrApp
2734+
* | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
27282735
*/
27292736
val constrApp: () => Tree = () => {
2730-
// Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code
2731-
val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR))
2732-
if (in.token == LPAREN) parArgumentExprss(wrapNew(t))
2733-
else t
2737+
2738+
def isAnnotType(t: Tree) = t match {
2739+
case _: Ident
2740+
| _: Select
2741+
| _: AppliedTypeTree
2742+
| _: Tuple
2743+
| _: Parens
2744+
| _: RefinedTypeTree
2745+
| _: SingletonTypeTree
2746+
| _: TypSplice
2747+
| _: Annotated => true
2748+
case _ => false
2749+
}
2750+
2751+
def givenArgs(t: Tree): Tree = {
2752+
if (in.token == GIVEN) givenArgs(applyGiven(t, prefixExpr)) else t
2753+
}
2754+
2755+
if (in.token == LPAREN)
2756+
inParens {
2757+
val t = toplevelTyp()
2758+
if (isAnnotType(t))
2759+
if (in.token == LPAREN) givenArgs(parArgumentExprss(wrapNew(t)))
2760+
else if (in.token == GIVEN) givenArgs(wrapNew(t))
2761+
else t
2762+
else Parens(t)
2763+
}
2764+
else {
2765+
val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR))
2766+
// Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code
2767+
if (in.token == LPAREN) parArgumentExprss(wrapNew(t)) else t
2768+
}
27342769
}
27352770

27362771
/** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1)

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
353353
keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
354354
else if (!ctx.settings.YprintDebug.value && fun.hasType && fun.symbol == defn.InternalQuoted_exprSplice)
355355
keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
356+
else if (tree.getAttachment(untpd.ApplyGiven).isDefined && !homogenizedView)
357+
changePrec(InfixPrec) {
358+
toTextLocal(fun) ~ " given " ~
359+
(if (args.length == 1) toTextLocal(args.head) else "(" ~ toTextGlobal(args, ", ") ~ ")")
360+
}
356361
else
357362
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
358363
case tree: TypeApply =>

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,6 +2588,8 @@ class Typer extends Namer
25882588
tree
25892589
else if (wtp.isContextual)
25902590
adaptNoArgs(wtp) // insert arguments implicitly
2591+
else if (tree.symbol.isPrimaryConstructor && tree.symbol.info.firstParamTypes.isEmpty)
2592+
readapt(tree.appliedToNone) // insert () to primary constructors
25912593
else
25922594
errorTree(tree, em"Missing arguments for $methodStr")
25932595
case _ => tryInsertApplyOrImplicit(tree, pt, locked) {

docs/docs/internals/syntax.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,8 +386,9 @@ Template ::= InheritClauses [TemplateBody]
386386
InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
387387
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
388388
| ConstrApp {‘,’ ConstrApp}
389-
ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
390-
| ‘(’ ConstrApp {‘given’ (InfixExpr | ParArgumentExprs)} ‘)’
389+
ConstrApp ::= SimpleConstrApp
390+
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
391+
SimpleConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
391392
ConstrExpr ::= SelfInvocation
392393
| ConstrBlock
393394
SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs}

docs/docs/reference/contextual-implicit/instance-defs.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ TmplDef ::= ...
7575
InstanceDef ::= [id] [DefTypeParamClause] InstanceBody
7676
InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
7777
| ‘for’ Type {GivenParamClause} ‘=’ Expr
78-
ConstrApp ::= AnnotType {ArgumentExprs}
79-
| ‘(’ ConstrApp {‘given’ (InfixExpr | ParArgumentExprs)} ‘)’
78+
ConstrApp ::= SimpleConstrApp
79+
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
80+
SimpleConstrApp ::= AnnotType {ArgumentExprs}
8081
GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes)
8182
GivenTypes ::= AnnotType {‘,’ AnnotType}
8283
```

docs/docs/reference/contextual/instance-defs.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes)
7272
InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody]
7373
| ‘for’ Type ‘=’ Expr
7474
GivenTypes ::= AnnotType {‘,’ AnnotType}
75+
ConstrApp ::= SimpleConstrApp
76+
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
7577
```
7678
The identifier `id` can be omitted only if either the `for` part or the template body is present.
7779
If the `for` part is missing, the template body must define at least one extension method.

tests/neg/parser-stability-25.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ class D extends (Int => 1) {
1111
}
1212

1313
class Wrap(x: Int)
14-
class E extends (Wrap)( // error
14+
class E extends (Wrap)( // error // error
1515
// error

tests/pos/given-constrapps.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class TC
2+
val tc = TC()
3+
class C given (x: TC) {
4+
assert(x eq tc)
5+
}
6+
class C2(n: Int) given (x: TC) given List[TC] {
7+
assert(x eq tc)
8+
the[List[TC]].foreach(t => assert(t eq tc))
9+
10+
def this() given TC given List[TC] = this(1)
11+
}
12+
13+
class D extends (C given tc)
14+
class D2 extends (C2(1) given tc given Nil)
15+
16+
class Foo given TC {
17+
assert(the[TC] != null)
18+
}
19+
20+
object Test extends App {
21+
new (C given tc)
22+
new (C() given tc)
23+
new (C given tc) {}
24+
new (C2(1) given tc given List(tc))
25+
new (C2(1) given tc given List(tc)) {}
26+
new (C2() given tc given List(tc))
27+
def foo given TC = ()
28+
foo given tc
29+
}

0 commit comments

Comments
 (0)