Skip to content

Commit 59a4b7d

Browse files
committed
New syntax for given defs
given [A: Ord] => A is Ord: ... given [A: Ord] => A is Ord as listOrd: ...
1 parent 9828026 commit 59a4b7d

File tree

4 files changed

+201
-16
lines changed

4 files changed

+201
-16
lines changed

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

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -948,19 +948,31 @@ object Parsers {
948948
* i.e. an identifier followed by type and value parameters, followed by `:`?
949949
* @pre The current token is an identifier
950950
*/
951-
def followingIsGivenSig() =
951+
def followingIsOldStyleGivenSig() =
952952
val lookahead = in.LookaheadScanner()
953953
if lookahead.isIdent then
954954
lookahead.nextToken()
955+
var paramsSeen = false
955956
def skipParams(): Unit =
956957
if lookahead.token == LPAREN || lookahead.token == LBRACKET then
958+
paramsSeen = true
957959
lookahead.skipParens()
958960
skipParams()
959961
else if lookahead.isNewLine then
960962
lookahead.nextToken()
961963
skipParams()
962964
skipParams()
963965
lookahead.isColon
966+
&& {
967+
!in.featureEnabled(Feature.modularity)
968+
|| { // with modularity language import, a `:` at EOL after an identifier represents a single identifier given
969+
// Example:
970+
// given C:
971+
// def f = ...
972+
lookahead.nextToken()
973+
!lookahead.isAfterLineEnd
974+
}
975+
}
964976

965977
def followingIsExtension() =
966978
val next = in.lookahead.token
@@ -1773,7 +1785,9 @@ object Parsers {
17731785

17741786
def infixTypeRest(t: Tree, operand: Location => Tree = refinedTypeFn): Tree =
17751787
infixOps(t, canStartInfixTypeTokens, operand, Location.ElseWhere, ParseKind.Type,
1776-
isOperator = !followingIsVararg() && !isPureArrow
1788+
isOperator = !followingIsVararg()
1789+
&& !isPureArrow
1790+
&& !(isIdent(nme.as) && in.featureEnabled(Feature.modularity))
17771791
&& nextCanFollowOperator(canStartInfixTypeTokens))
17781792

17791793
/** RefinedType ::= WithType {[nl] Refinement} [`^` CaptureSet]
@@ -4059,15 +4073,30 @@ object Parsers {
40594073
syntaxError(em"extension clause can only define methods", stat.span)
40604074
}
40614075

4062-
/** GivenDef ::= [GivenSig] (GivenType [‘=’ Expr] | StructuralInstance)
4063-
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
4064-
* GivenType ::= AnnotType1 {id [nl] AnnotType1}
4076+
/** GivenDef ::= OldGivenDef | NewGivenDef
4077+
* OldGivenDef ::= [OldGivenSig] (GivenType [‘=’ Expr] | StructuralInstance)
4078+
* OldGivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
40654079
* StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody]
4080+
*
4081+
* NewGivenDef ::= [GivenConditional '=>'] NewGivenSig
4082+
* GivenConditional ::= [DefTypeParamClause | UsingParamClause] {UsingParamClause}
4083+
* NewGivenSig ::= GivenType ['as' id] ([‘=’ Expr] | TemplateBody)
4084+
* | ConstrApps ['as' id] TemplateBody
4085+
*
4086+
* GivenType ::= AnnotType1 {id [nl] AnnotType1}
40664087
*/
40674088
def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) {
40684089
var mods1 = addMod(mods, givenMod)
40694090
val nameStart = in.offset
4070-
val name = if isIdent && followingIsGivenSig() then ident() else EmptyTermName
4091+
var name = if isIdent && followingIsOldStyleGivenSig() then ident() else EmptyTermName
4092+
var newSyntaxAllowed = in.featureEnabled(Feature.modularity)
4093+
4094+
def moreConstrApps() =
4095+
if newSyntaxAllowed && in.token == COMMA then
4096+
in.nextToken()
4097+
constrApps()
4098+
else // need to be careful with last `with`
4099+
withConstrApps()
40714100

40724101
// TODO Change syntax description
40734102
def adjustDefParams(paramss: List[ParamClause]): List[ParamClause] =
@@ -4086,14 +4115,24 @@ object Parsers {
40864115
else Nil
40874116
newLinesOpt()
40884117
val noParams = tparams.isEmpty && vparamss.isEmpty
4089-
if !(name.isEmpty && noParams) then acceptColon()
4118+
if !(name.isEmpty && noParams) then
4119+
if in.isColon then
4120+
newSyntaxAllowed = false
4121+
in.nextToken()
4122+
else if newSyntaxAllowed then accept(ARROW)
4123+
else acceptColon()
40904124
val parents =
40914125
if isSimpleLiteral then
40924126
rejectWildcardType(annotType()) :: Nil
40934127
else constrApp() match
4094-
case parent: Apply => parent :: withConstrApps()
4095-
case parent if in.isIdent => infixTypeRest(parent, _ => annotType1()) :: Nil
4096-
case parent => parent :: withConstrApps()
4128+
case parent: Apply => parent :: moreConstrApps()
4129+
case parent if in.isIdent =>
4130+
infixTypeRest(parent, _ => annotType1()) :: Nil
4131+
case parent => parent :: moreConstrApps()
4132+
if newSyntaxAllowed && in.isIdent(nme.as) then
4133+
in.nextToken()
4134+
name = ident()
4135+
40974136
val parentsIsType = parents.length == 1 && parents.head.isType
40984137
if in.token == EQUALS && parentsIsType then
40994138
accept(EQUALS)
@@ -4103,7 +4142,7 @@ object Parsers {
41034142
ValDef(name, parents.head, subExpr())
41044143
else
41054144
DefDef(name, adjustDefParams(joinParams(tparams, vparamss)), parents.head, subExpr())
4106-
else if (isStatSep || isStatSeqEnd) && parentsIsType then
4145+
else if (isStatSep || isStatSeqEnd) && parentsIsType && !newSyntaxAllowed then
41074146
if name.isEmpty then
41084147
syntaxError(em"anonymous given cannot be abstract")
41094148
DefDef(name, adjustDefParams(joinParams(tparams, vparamss)), parents.head, EmptyTree)
@@ -4114,8 +4153,13 @@ object Parsers {
41144153
else vparam
41154154
val constr = makeConstructor(tparams, vparamss1)
41164155
val templ =
4117-
if isStatSep || isStatSeqEnd then Template(constr, parents, Nil, EmptyValDef, Nil)
4118-
else withTemplate(constr, parents)
4156+
if isStatSep || isStatSeqEnd then
4157+
Template(constr, parents, Nil, EmptyValDef, Nil)
4158+
else if !newSyntaxAllowed || in.token == WITH then
4159+
withTemplate(constr, parents)
4160+
else
4161+
possibleTemplateStart()
4162+
templateBodyOpt(constr, parents, Nil)
41194163
if noParams && !mods.is(Inline) then ModuleDef(name, templ)
41204164
else TypeDef(name.toTypeName, templ)
41214165
end gdef

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,7 @@ i15525.scala
121121
# alias types at different levels of dereferencing
122122
parsercombinators-givens.scala
123123
parsercombinators-givens-2.scala
124+
parsercombinators-arrow.scala
125+
124126

125127

docs/_docs/internals/syntax.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -459,10 +459,13 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
459459
ConstrMods ::= {Annotation} [AccessModifier]
460460
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
461461
EnumDef ::= id ClassConstr InheritClauses EnumBody
462-
GivenDef ::= [GivenSig] (GivenType [‘=’ Expr] | StructuralInstance)
463-
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present
462+
463+
GivenDef ::= [GivenConditional '=>'] GivenSig
464+
GivenConditional ::= [DefTypeParamClause | UsingParamClause] {UsingParamClause}
465+
GivenSig ::= GivenType ['as' id] ([‘=’ Expr] | TemplateBody)
466+
| ConstrApps ['as' id] TemplateBody
464467
GivenType ::= AnnotType1 {id [nl] AnnotType1}
465-
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody]
468+
466469
Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause}
467470
‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods
468471
ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>>

tests/pos/typeclasses-arrow0.scala

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//> using options -language:experimental.modularity -source future
2+
3+
class Common:
4+
5+
trait Ord[A]:
6+
extension (x: A)
7+
def compareTo(y: A): Int
8+
def < (y: A): Boolean = compareTo(y) < 0
9+
def > (y: A): Boolean = compareTo(y) > 0
10+
def <= (y: A): Boolean = compareTo(y) <= 0
11+
def >= (y: A): Boolean = compareTo(y) >= 0
12+
def max(y: A): A = if x < y then y else x
13+
14+
trait Show[A]:
15+
extension (x: A) def show: String
16+
17+
trait SemiGroup[A]:
18+
extension (x: A) def combine(y: A): A
19+
20+
trait Monoid[A] extends SemiGroup[A]:
21+
def unit: A
22+
23+
trait Functor[F[_]]:
24+
extension [A](x: F[A]) def map[B](f: A => B): F[B]
25+
26+
trait Monad[F[_]] extends Functor[F]:
27+
def pure[A](x: A): F[A]
28+
extension [A](x: F[A])
29+
def flatMap[B](f: A => F[B]): F[B]
30+
def map[B](f: A => B) = x.flatMap(f `andThen` pure)
31+
end Common
32+
33+
object Instances extends Common:
34+
35+
given Ord[Int] as intOrd:
36+
extension (x: Int)
37+
def compareTo(y: Int) =
38+
if x < y then -1
39+
else if x > y then +1
40+
else 0
41+
42+
given [T: Ord] => Ord[List[T]]:
43+
extension (xs: List[T]) def compareTo(ys: List[T]): Int = (xs, ys) match
44+
case (Nil, Nil) => 0
45+
case (Nil, _) => -1
46+
case (_, Nil) => +1
47+
case (x :: xs1, y :: ys1) =>
48+
val fst = x.compareTo(y)
49+
if (fst != 0) fst else xs1.compareTo(ys1)
50+
51+
given Monad[List] as listMonad:
52+
extension [A](xs: List[A]) def flatMap[B](f: A => List[B]): List[B] =
53+
xs.flatMap(f)
54+
def pure[A](x: A): List[A] =
55+
List(x)
56+
57+
type Reader[Ctx] = [X] =>> Ctx => X
58+
59+
given [Ctx] => Monad[Reader[Ctx]] as readerMonad:
60+
extension [A](r: Ctx => A) def flatMap[B](f: A => Ctx => B): Ctx => B =
61+
ctx => f(r(ctx))(ctx)
62+
def pure[A](x: A): Ctx => A =
63+
ctx => x
64+
65+
extension (xs: Seq[String])
66+
def longestStrings: Seq[String] =
67+
val maxLength = xs.map(_.length).max
68+
xs.filter(_.length == maxLength)
69+
70+
extension [T](xs: List[T])
71+
def second = xs.tail.head
72+
def third = xs.tail.tail.head
73+
74+
extension [M[_]: Monad, A](xss: M[M[A]])
75+
def flatten: M[A] =
76+
xss.flatMap(identity)
77+
78+
def maximum[T: Ord](xs: List[T]): T =
79+
xs.reduce(_ `max` _)
80+
81+
given [T: Ord] => Ord[T] as descending:
82+
extension (x: T) def compareTo(y: T) = summon[Ord[T]].compareTo(y)(x)
83+
84+
def minimum[T: Ord](xs: List[T]) =
85+
maximum(xs)(using descending)
86+
87+
def test(): Unit =
88+
val xs = List(1, 2, 3)
89+
println(maximum(xs))
90+
println(maximum(xs)(using descending))
91+
println(maximum(xs)(using descending(using intOrd)))
92+
println(minimum(xs))
93+
94+
// Adapted from the Rust by Example book: https://doc.rust-lang.org/rust-by-example/trait.html
95+
//
96+
// lines words chars
97+
// wc Scala: 28 105 793
98+
// wc Rust : 57 193 1466
99+
100+
trait Animal[Self]:
101+
102+
// Associated function signature; `Self` refers to the implementor type.
103+
def apply(name: String): Self
104+
105+
// Method signatures; these will return a string.
106+
extension (self: Self)
107+
def name: String
108+
def noise: String
109+
def talk(): Unit = println(s"$name, $noise")
110+
end Animal
111+
112+
class Sheep(val name: String):
113+
var isNaked = false
114+
def shear() =
115+
if isNaked then
116+
println(s"$name is already naked...")
117+
else
118+
println(s"$name gets a haircut!")
119+
isNaked = true
120+
121+
given Animal[Sheep]:
122+
def apply(name: String) = Sheep(name)
123+
extension (self: Sheep)
124+
def name: String = self.name
125+
def noise: String = if self.isNaked then "baaaaah?" else "baaaaah!"
126+
override def talk(): Unit =
127+
println(s"$name pauses briefly... $noise")
128+
129+
/*
130+
131+
- In a type pattern, A <: T, A >: T, A: T, A: _ are all allowed and mean
132+
T is a fresh type variable (T can start with a capital letter).
133+
- instance definitions
134+
- `as m` syntax in context bounds and instance definitions
135+
136+
*/

0 commit comments

Comments
 (0)