Skip to content

Commit

Permalink
Switch to with syntax for context parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Jan 23, 2020
1 parent 762c644 commit 04095bc
Show file tree
Hide file tree
Showing 473 changed files with 1,042 additions and 1,030 deletions.
62 changes: 41 additions & 21 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -938,10 +938,13 @@ object Parsers {
lookahead.nextToken()
while lookahead.token == LPAREN || lookahead.token == LBRACKET do
lookahead.skipParens()
if lookahead.token == COLON then
if lookahead.token == COLON then // TODO: remove
lookahead.nextToken()
!lookahead.isAfterLineEnd
else lookahead.token == SUBTYPE || lookahead.isIdent(nme.as)
else
lookahead.token == SUBTYPE // TODO: remove
|| lookahead.isIdent(nme.as)
|| lookahead.token == WITH && lookahead.ch != Chars.LF // TODO: remove LF test

def followingIsExtension() =
val lookahead = in.LookaheadScanner()
Expand Down Expand Up @@ -2106,6 +2109,7 @@ object Parsers {
* | SimpleExpr `.' MatchClause
* | SimpleExpr (TypeArgs | NamedTypeArgs)
* | SimpleExpr1 ArgumentExprs
* | SimpleExpr ContextArguments
* Quoted ::= ‘'’ ‘{’ Block ‘}’
* | ‘'’ ‘[’ Type ‘]’
*/
Expand Down Expand Up @@ -2174,6 +2178,8 @@ object Parsers {
case DOT =>
in.nextToken()
simpleExprRest(selector(t), canApply = true)
case DOTWITH =>
simpleExprRest(contextArguments(t), canApply = true)
case LBRACKET =>
val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) }
simpleExprRest(tapp, canApply = true)
Expand Down Expand Up @@ -2250,7 +2256,7 @@ object Parsers {
else fn
}

/** ParArgumentExprss ::= {ParArgumentExprs}
/** ParArgumentExprss ::= {ParArgumentExprs | ContextArguments}
*
* Special treatment for arguments to primary constructor annotations.
* (...) is considered an argument only if it does not look like a formal
Expand All @@ -2275,6 +2281,8 @@ object Parsers {
parArgumentExprss(
atSpan(startOffset(fn)) { mkApply(fn, parArgumentExprs()) }
)
else if in.token == DOTWITH then
parArgumentExprss(contextArguments(fn))
else fn
}

Expand Down Expand Up @@ -2304,6 +2312,14 @@ object Parsers {
else Block(stats, EmptyTree)
}

/** ContextArguments ::= ‘.’ ‘with’ ArgumentExprs */
def contextArguments(t: Tree): Tree =
if in.token == DOTWITH then
atSpan(t.span.start, in.skipToken()) {
Apply(t, argumentExprs()._1).setGivenApply()
}
else t

/** Guard ::= if PostfixExpr
*/
def guard(): Tree =
Expand Down Expand Up @@ -2786,23 +2802,23 @@ object Parsers {
def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] =
if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil

/** OLD: GivenTypes ::= AnnotType {‘,’ AnnotType}
* NEW: GivenTypes ::= Type {‘,’ Type}
/** AnnotTypes ::= AnnotType {‘,’ AnnotType}
* Types ::= Type {‘,’ Type}
*/
def givenTypes(nparams: Int, ofClass: Boolean): List[ValDef] =
val tps = commaSeparated(typ)
def givenTypes(parseType: () => Tree, nparams: Int, ofClass: Boolean): List[ValDef] =
val tps = commaSeparated(parseType)
var counter = nparams
def nextIdx = { counter += 1; counter }
val paramFlags = if ofClass then Private | Local | ParamAccessor else Param
tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given))

/** ClsParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’
* GivenClsParamClause::= 'with' ‘(’ (ClsParams | GivenTypes) ‘)’
* GivenClsParamClause::= 'with' (‘(’ (ClsParams | Types) ‘)’ | AnnotTypes)
* ClsParams ::= ClsParam {‘,’ ClsParam}
* ClsParam ::= {Annotation}
*
* DefParamClause ::= ‘(’ [‘erased’] DefParams ‘)’
* GivenParamClause ::= ‘with’ ‘(’ (DefParams | GivenTypes) ‘)’
* GivenParamClause ::= ‘with’ (‘(’ (DefParams | Types) ‘)’ | AnnotTypes)
* DefParams ::= DefParam {‘,’ DefParam}
* DefParam ::= {Annotation} [‘inline’] Param
*
Expand Down Expand Up @@ -2888,34 +2904,33 @@ object Parsers {
impliedModOpt(GIVEN, () => Mod.Given())
impliedModOpt(ERASED, () => Mod.Erased())
if givenOnly && !impliedMods.is(Given) then
syntaxError(ExpectedTokenButFound(GIVEN, in.token))
syntaxError("Normal parameter clause cannot follow context parameter clause")
val isParams =
!impliedMods.is(Given)
|| startParamTokens.contains(in.token)
|| isIdent && (in.name == nme.inline || in.lookaheadIn(BitSet(COLON)))
if isParams then commaSeparated(() => param())
else givenTypes(nparams, ofClass)
else givenTypes(typ, nparams, ofClass)
checkVarArgsRules(clause)
clause
}
}

/** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
* | {ClsParamClause | GivenClsParamClause} [‘with’ GivenTypes]
* | {ClsParamClause} {GivenClsParamClause}
* DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
* | {DefParamClause | GivenParamClause} [‘with’ GivenTypes]
* GivenParamClauses ::= {GivenParamClause} [‘with’ GivenTypes]
* | {DefParamClause} {GivenParamClause}
*
* @return The parameter definitions
*/
def paramClauses(ofClass: Boolean = false,
ofCaseClass: Boolean = false,
givenOnly: Boolean = false): List[List[ValDef]] =

def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] =
def recur(firstClause: Boolean, nparams: Int, givenOnly: Boolean): List[List[ValDef]] =
newLineOptWhenFollowedBy(LPAREN)
val prefixMods =
if in.token == WITH then
if in.token == WITH && in.ch != Chars.LF then // TODO: remove LF test
in.nextToken()
Modifiers(Given)
else
Expand All @@ -2930,15 +2945,18 @@ object Parsers {
firstClause = firstClause,
prefixMods = prefixMods)
val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit)
val isGivenClause = prefixMods.is(Given)
|| params.nonEmpty && params.head.mods.flags.is(Given)
params :: (
if lastClause then Nil
else recur(firstClause = false, nparams + params.length))
else recur(firstClause = false, nparams + params.length, givenOnly | isGivenClause))
else if prefixMods.is(Given) then
givenTypes(nparams, ofClass) :: Nil
val params = givenTypes(annotType, nparams, ofClass)
params :: recur(firstClause = false, nparams + params.length, true)
else Nil
end recur

recur(firstClause = true, 0)
recur(firstClause = true, 0, givenOnly)
end paramClauses

/* -------- DEFS ------------------------------------------- */
Expand Down Expand Up @@ -3464,7 +3482,8 @@ object Parsers {
val tparams = typeParamClauseOpt(ParamOwner.Def)
val paramsStart = in.offset
val vparamss =
if in.token == WITH || in.token == LPAREN && followingIsParamOrGivenType()
if in.token == WITH && in.ch != Chars.LF // TODO: remove LF test
|| in.token == LPAREN && followingIsParamOrGivenType()
then paramClauses()
else Nil
def checkAllGivens(vparamss: List[List[ValDef]], what: String) =
Expand Down Expand Up @@ -3531,7 +3550,8 @@ object Parsers {
val constrApp: () => Tree = () => {
val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR))
// Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code
if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t
if in.token == LPAREN || in.token == DOTWITH then parArgumentExprss(wrapNew(t))
else t
}

/** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}
Expand Down
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,11 @@ object Scanners {
this.copyFrom(prev)
}

/** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE, COLON + <EOL> => COLONEOL
/** - Join CASE + CLASS => CASECLASS,
* CASE + OBJECT => CASEOBJECT,
* SEMI + ELSE => ELSE,
* COLON + <EOL> => COLONEOL
* DOT + WITH => DOTWITH
* - Insert missing OUTDENTs at EOF
*/
def postProcessToken(): Unit = {
Expand All @@ -619,6 +623,10 @@ object Scanners {
} else if (token == EOF) { // e.g. when the REPL is parsing "val List(x, y, _*,"
/* skip the trailing comma */
} else reset()
case DOT =>
lookahead()
if token == WITH then fuse(DOTWITH)
else reset()
case COLON =>
if colonSyntax then observeColonEOL()
case EOF | RBRACE =>
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ object Tokens extends TokensCommon {
/** special symbols */
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
final val NEWLINES = 79; enter(NEWLINES, "end of statement", "new lines")
final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")

/** special keywords */
final val USCORE = 73; enter(USCORE, "_")
Expand All @@ -204,6 +203,9 @@ object Tokens extends TokensCommon {

final val QUOTE = 87; enter(QUOTE, "'")

final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
final val DOTWITH = 89; enter(DOTWITH, ".with")

/** XML mode */
final val XMLSTART = 98; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate

Expand Down
2 changes: 1 addition & 1 deletion compiler/test-resources/repl/3932
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scala> def fun[T](x: T): (given List[T]) => Int = ???
def fun[T](x: T): (given List[T]) => Int
def fun[T](x: T): (List[T]) ?=> Int
27 changes: 15 additions & 12 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ NamedTypeArgs ::= ‘[’ NamedTypeArg {‘,’ NamedTypeArg} ‘]’
Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ds
SubtypeBounds ::= [‘>:’ Type] [‘<:’ Type] | INT TypeBoundsTree(lo, hi)
TypeParamBounds ::= SubtypeBounds {‘:’ Type} ContextBounds(typeBounds, tps)
Types ::= Type {‘,’ Type}
AnnotTypes ::= AnnotType {‘,’ AnnotType}
```

### Expressions
Expand Down Expand Up @@ -205,7 +207,7 @@ PostfixExpr ::= InfixExpr [id]
InfixExpr ::= PrefixExpr
| InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr)
| InfixExpr MatchClause
MatchClause ::= ‘match’ ‘{’ CaseClauses ‘}’ Match(expr, cases)
MatchClause ::= ‘match’ ‘{’ CaseClauses ‘}’ Match(expr, cases)
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op)
SimpleExpr ::= Path
| Literal
Expand All @@ -219,6 +221,7 @@ SimpleExpr ::= Path
| ‘(’ ExprsInParens ‘)’ Parens(exprs)
| SimpleExpr ‘.’ id Select(expr, id)
| SimpleExpr ‘.’ MatchClause
| SimpleExpr ‘.’ ContextArguments
| SimpleExpr TypeArgs TypeApply(expr, args)
| SimpleExpr ArgumentExprs Apply(expr, args)
| SimpleExpr ‘_’ PostfixOp(expr, _)
Expand All @@ -228,10 +231,12 @@ Quoted ::= ‘'’ ‘{’ Block ‘}’
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
| Expr
ParArgumentExprs ::= ‘(’ [‘given’] ExprsInParens ‘)’ exprs
ParArgumentExprs ::= ‘(’ ExprsInParens ‘)’ exprs
| ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ exprs :+ Typed(expr, Ident(wildcardStar))
ParArgumentExprss ::= {ParArgumentExprs | ContextArguments}
ArgumentExprs ::= ParArgumentExprs
| [nl] BlockExpr
ContextArguments ::= ‘.’ ‘with’ ArgumentExprs
BlockExpr ::= ‘{’ (CaseClauses | Block) ‘}’
Block ::= {BlockStat semi} [BlockResult] Block(stats, expr?)
BlockStat ::= Import
Expand Down Expand Up @@ -293,23 +298,21 @@ HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] |
SubtypeBounds
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
| {ClsParamClause | GivenClsParamClause} [‘with’ GivenTypes]
| {ClsParamClause} {GivenClsParamClause}
ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’
GivenClsParamClause::= ‘with’ ‘(’ (ClsParams | GivenTypes) ‘)’
GivenClsParamClause::= ‘with’ (‘(’ (ClsParams | Types) ‘)’ | AnnotTypes)
ClsParams ::= ClsParam {‘,’ ClsParam}
ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var
[{Modifier} (‘val’ | ‘var’) | ‘inline’] Param
Param ::= id ‘:’ ParamType [‘=’ Expr]
| INT
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
| {DefParamClause | GivenParamClause} [‘with’ GivenTypes]
| {DefParamClause} {GivenParamClause}
DefParamClause ::= [nl] ‘(’ DefParams ‘)’
GivenParamClause ::= ‘with’ ‘(’ (DefParams | GivenTypes) ‘)’
GivenParamClauses ::= {GivenParamClause} [‘with’ GivenTypes]
GivenParamClause ::= ‘with’ (‘(’ (DefParams | Types) ‘)’ | AnnotTypes)
DefParams ::= DefParam {‘,’ DefParam}
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
GivenTypes ::= Type {‘,’ Type}
ClosureMods ::= { ‘implicit’ | ‘given’}
```

Expand All @@ -332,7 +335,7 @@ LocalModifier ::= ‘abstract’
AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier]
AccessQualifier ::= ‘[’ id ‘]’
Annotation ::= ‘@’ SimpleType {ParArgumentExprs} Apply(tpe, args)
Annotation ::= ‘@’ SimpleType ParArgumentExprss Apply(tpe, args)
Import ::= ‘import’ ImportExpr {‘,’ ImportExpr}
ImportExpr ::= StableId ‘.’ ImportSpec Import(expr, sels)
Expand Down Expand Up @@ -387,14 +390,14 @@ ObjectDef ::= id [Template]
EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template)
GivenDef ::= [GivenSig] [‘_’ ‘<:’] Type ‘=’ Expr
| [GivenSig] ConstrApps [TemplateBody]
GivenSig ::= [id] [DefTypeParamClause] GivenParamClauses ‘as’
ExtensionDef ::= [id] ‘on’ ExtParamClause GivenParamClauses ExtMethods
GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause} ‘as’
ExtensionDef ::= [id] ‘on’ ExtParamClause {GivenParamClause} ExtMethods
ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’
Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats)
InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}
ConstrApp ::= AnnotType {ParArgumentExprs} Apply(tp, args)
ConstrApp ::= AnnotType ParArgumentExprss Apply(tp, args)
ConstrExpr ::= SelfInvocation
| ‘{’ SelfInvocation {semi BlockStat} ‘}’
SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import scala.quoted.autolift.given
object Macros {

inline def foo(i: => Int): Int = ${ fooImpl('i) }
def fooImpl(i: Expr[Int])(given QuoteContext): Expr[Int] = {
def fooImpl(i: Expr[Int]) with QuoteContext : Expr[Int] = {
given Toolbox = Toolbox.make(getClass.getClassLoader)
val y: Int = run(i)
y
Expand Down
4 changes: 2 additions & 2 deletions tests/disabled/reflect/run/t3425b/Base_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ object Gen {
}
case class Pair(tp1: Tp, tp2: Tp) {
def expr = s"((new ABC): $tp)"
def tp = s"($tp1) with ($tp2)"
def tp = s"($tp1) with $tp2"
}
val traits = Vector("Any", "A", "B", "C") map ("%6s" format _)
val types = Vector("P", "Q", "R forSome { type R <: P with Q }")
Expand All @@ -39,7 +39,7 @@ object Gen {
import p._
List(
s"type R1_$idx = $tp",
s"type R2_$idx = R1_$idx { val y: (${tp1.elem}) with (${tp2.elem}) }"
s"type R2_$idx = R1_$idx { val y: (${tp1.elem}) with ${tp2.elem} }"
)
}

Expand Down
2 changes: 1 addition & 1 deletion tests/generic-java-signatures/i3653.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Foo {
c0: T, c1: T, c2: T, c3: T, c4: T, c5: T, c6: T, c7: T, c8: T, c9: T) => 0

// #6946
def baz = (x: String ?=> Unit) => x(given "")
def baz = (x: String ?=> Unit) => x.with("")
}

object Test {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import scala.quoted.QuoteContext

def test(given QuoteContext) = {
def test with QuoteContext = {
val x = '{0}
val y = '{ // error: Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.
$x
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/GenericNumLits/Even_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object Even {
else throw FromDigits.MalformedNumber(s"$digits is odd")
}

private def evenFromDigitsImpl(digits: Expr[String])(given ctx: QuoteContext): Expr[Even] = digits match {
private def evenFromDigitsImpl(digits: Expr[String]) with (ctx: QuoteContext) : Expr[Even] = digits match {
case Const(ds) =>
val ev =
try evenFromDigits(ds)
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/delegate-match-1/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import scala.quoted.matching._

inline def f: Any = ${ fImpl }

private def fImpl(given qctx: QuoteContext): Expr[Unit] = {
private def fImpl with (qctx: QuoteContext) : Expr[Unit] = {
import qctx.tasty.{_, given}
searchImplicit(('[A]).unseal.tpe) match {
case x: ImplicitSearchSuccess =>
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/delegate-match-2/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import scala.quoted.matching._

inline def f: Any = ${ fImpl }

private def fImpl(given qctx: QuoteContext): Expr[Unit] = {
private def fImpl with (qctx: QuoteContext) : Expr[Unit] = {
import qctx.tasty.{_, given}
searchImplicit(('[A]).unseal.tpe) match {
case x: ImplicitSearchSuccess =>
Expand Down
Loading

0 comments on commit 04095bc

Please sign in to comment.