Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use given for implicit parameters and arguments #5821

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Implicit()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.ImplicitCommon)

case class Given()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.ImplicitCommon | Flags.Contextual)

case class Erased()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Erased)

case class Final()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Final)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class Definitions {
* ImplicitFunctionN traits follow this template:
*
* trait ImplicitFunctionN[T0,...,T{N-1}, R] extends Object {
* def apply with ($x0: T0, ..., $x{N_1}: T{N-1}): R
* def apply given ($x0: T0, ..., $x{N_1}: T{N-1}): R
* }
*
* ErasedFunctionN traits follow this template:
Expand All @@ -112,7 +112,7 @@ class Definitions {
* ErasedImplicitFunctionN traits follow this template:
*
* trait ErasedImplicitFunctionN[T0,...,T{N-1}, R] extends Object {
* def apply with (erased $x0: T0, ..., $x{N_1}: T{N-1}): R
* def apply given (erased $x0: T0, ..., $x{N_1}: T{N-1}): R
* }
*
* ErasedFunctionN and ErasedImplicitFunctionN erase to Function0.
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3879,11 +3879,11 @@ object Types {
def selfType(implicit ctx: Context): Type = {
if (selfTypeCache == null)
selfTypeCache = {
val given = cls.givenSelfType
if (!given.isValueType) appliedRef
else if (cls is Module) given
val givenSelf = cls.givenSelfType
if (!givenSelf.isValueType) appliedRef
else if (cls is Module) givenSelf
else if (ctx.erasedTypes) appliedRef
else AndType(given, appliedRef)
else AndType(givenSelf, appliedRef)
}
selfTypeCache
}
Expand Down
100 changes: 41 additions & 59 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -364,22 +364,22 @@ object Parsers {

/** Convert tree to formal parameter list
*/
def convertToParams(tree: Tree, mods: Modifiers): List[ValDef] = tree match {
case Parens(t) => convertToParam(t, mods) :: Nil
case Tuple(ts) => ts map (convertToParam(_, mods))
case t => convertToParam(t, mods) :: Nil
def convertToParams(tree: Tree): List[ValDef] = tree match {
case Parens(t) => convertToParam(t) :: Nil
case Tuple(ts) => ts map (convertToParam(_))
case t => convertToParam(t) :: Nil
}

/** Convert tree to formal parameter
*/
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match {
def convertToParam(tree: Tree, expected: String = "formal parameter"): ValDef = tree match {
case Ident(name) =>
makeParameter(name.asTermName, TypeTree(), mods).withSpan(tree.span)
makeParameter(name.asTermName, TypeTree()).withSpan(tree.span)
case Typed(Ident(name), tpt) =>
makeParameter(name.asTermName, tpt, mods).withSpan(tree.span)
makeParameter(name.asTermName, tpt).withSpan(tree.span)
case _ =>
syntaxError(s"not a legal $expected", tree.span)
makeParameter(nme.ERROR, tree, mods)
makeParameter(nme.ERROR, tree)
}

/** Convert (qual)ident to type identifier
Expand Down Expand Up @@ -535,7 +535,7 @@ object Parsers {
}
else recur(operand())
}
else if (in.token == WITH) {
else if (in.token == GIVEN) {
val top1 = reduceStack(base, top, minInfixPrec, leftAssoc = true, nme.WITHkw, isType)
assert(opStack `eq` base)
val app = atSpan(startOffset(top1), in.offset) {
Expand Down Expand Up @@ -770,7 +770,7 @@ object Parsers {
*/
def toplevelTyp(): Tree = checkWildcard(typ())

/** Type ::= [‘erased’] FunArgTypes (‘=>’ | ‘|=>’) Type
/** Type ::= FunTypeMods FunArgTypes `=>' Type
* | HkTypeParamClause `->' Type
* | InfixType
* FunArgTypes ::= InfixType
Expand All @@ -779,20 +779,11 @@ object Parsers {
*/
def typ(): Tree = {
val start = in.offset
val imods = modifiers(BitSet(ERASED))
val imods = modifiers(funTypeMods)
def functionRest(params: List[Tree]): Tree =
atSpan(start, in.offset) {
val pmods =
if (in.token == CARROW) {
in.nextToken()
imods | (Contextual | Implicit)
}
else {
accept(ARROW)
imods
}
atSpan(start, accept(ARROW)) {
val t = typ()
if (pmods.flags.is(Implicit | Contextual | Erased)) new FunctionWithMods(params, t, pmods)
if (imods.is(Implicit | Contextual | Erased)) new FunctionWithMods(params, t, imods)
else Function(params, t)
}
def funArgTypesRest(first: Tree, following: () => Tree) = {
Expand Down Expand Up @@ -826,7 +817,7 @@ object Parsers {
}
openParens.change(LPAREN, -1)
accept(RPAREN)
if (imods.is(Implicit) || isValParamList || in.token == ARROW || in.token == CARROW)
if (imods.is(Implicit) || isValParamList || in.token == ARROW)
functionRest(ts)
else {
val ts1 =
Expand Down Expand Up @@ -858,7 +849,7 @@ object Parsers {
else infixType()

in.token match {
case ARROW | CARROW => functionRest(t :: Nil)
case ARROW => functionRest(t :: Nil)
case MATCH => matchType(EmptyTree, t)
case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t
case _ =>
Expand Down Expand Up @@ -1134,15 +1125,14 @@ object Parsers {
}
}

/** Expr ::= [FunArgMods] FunParams =>' Expr
* | [‘erased’] FunParams ‘|=>’ Expr
/** Expr ::= [ClosureMods] FunParams =>' Expr
* | Expr1
* FunParams ::= Bindings
* | id
* | `_'
* ExprInParens ::= PostfixExpr `:' Type
* | Expr
* BlockResult ::= [FunArgMods] FunParams =>' Block
* BlockResult ::= [ClosureMods] FunParams =>' Block
* | Expr1
* Expr1 ::= [‘inline’] `if' `(' Expr `)' {nl} Expr [[semi] else Expr]
* | [‘inline’] `if' Expr `then' Expr [[semi] else Expr]
Expand Down Expand Up @@ -1171,8 +1161,8 @@ object Parsers {

def expr(location: Location.Value): Tree = {
val start = in.offset
if (in.token == IMPLICIT || in.token == ERASED) {
val imods = modifiers(funArgMods)
if (in.token == IMPLICIT || in.token == ERASED || in.token == GIVEN) {
val imods = modifiers(closureMods)
if (in.token == MATCH) implicitMatch(start, imods)
else implicitClosure(start, location, imods)
} else {
Expand All @@ -1185,11 +1175,9 @@ object Parsers {
finally placeholderParams = saved

val t = expr1(location)
if (in.token == ARROW || in.token == CARROW) {
if (in.token == ARROW) {
placeholderParams = Nil // don't interpret `_' to the left of `=>` as placeholder
val impliedMods =
if (in.token == CARROW) Modifiers(Implicit | Contextual) else EmptyModifiers
wrapPlaceholders(closureRest(start, location, convertToParams(t, impliedMods)))
wrapPlaceholders(closureRest(start, location, convertToParams(t)))
}
else if (isWildcard(t)) {
placeholderParams = placeholderParams ::: saved
Expand Down Expand Up @@ -1407,28 +1395,16 @@ object Parsers {
}
else ident()

/** Expr ::= FunArgMods FunParams `=>' Expr
* | [‘erased’] FunParams ‘|=>’ Expr
/** Expr ::= ClosureMods FunParams `=>' Expr
* BlockResult ::= implicit id [`:' InfixType] `=>' Block // Scala2 only
*/
def implicitClosure(start: Int, location: Location.Value, implicitMods: Modifiers): Tree =
closureRest(start, location, funParams(implicitMods, location))

def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree =
atSpan(start, in.offset) {
val params1 =
if (in.token == CARROW) {
in.nextToken()
params.map {
case param: ValDef => param.withMods(param.mods | (Implicit | Contextual))
case param => param
}
}
else {
accept(ARROW)
params
}
Function(params1, if (location == Location.InBlock) block() else expr())
accept(ARROW)
Function(params, if (location == Location.InBlock) block() else expr())
}

/** PostfixExpr ::= InfixExpr [id [nl]]
Expand Down Expand Up @@ -1853,6 +1829,7 @@ object Parsers {
case ABSTRACT => Mod.Abstract()
case FINAL => Mod.Final()
case IMPLICIT => Mod.Implicit()
case GIVEN => Mod.Given()
case ERASED => Mod.Erased()
case LAZY => Mod.Lazy()
case OVERRIDE => Mod.Override()
Expand Down Expand Up @@ -1949,9 +1926,13 @@ object Parsers {
normalize(loop(start))
}

/** FunArgMods ::= { `implicit` | `erased` }
/** FunArgMods ::= { `implicit` | `erased` }
* ClosureMods ::= { ‘implicit’ | ‘erased’ | ‘given’}
* FunTypeMods ::= { ‘erased’ | ‘given’}
*/
def funArgMods: BitSet = BitSet(IMPLICIT, ERASED)
val funArgMods: BitSet = BitSet(IMPLICIT, ERASED)
val closureMods: BitSet = BitSet(GIVEN, IMPLICIT, ERASED)
val funTypeMods: BitSet = BitSet(GIVEN, ERASED)

/** Wrap annotation or constructor in New(...).<init> */
def wrapNew(tpt: Tree): Select = Select(New(tpt), nme.CONSTRUCTOR)
Expand Down Expand Up @@ -2038,7 +2019,7 @@ object Parsers {
* ClsParams ::= ClsParam {`' ClsParam}
* ClsParam ::= {Annotation} [{Modifier} (`val' | `var') | `inline'] Param
* DefParamClause ::= [nl] `(' [FunArgMods] [DefParams] ')' | InstParamClause
* InstParamClause ::= ‘with’ (‘(’ [DefParams] ‘)’ | ContextTypes)
* InstParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | ContextTypes)
* ContextTypes ::= RefinedType {`,' RefinedType}
* DefParams ::= DefParam {`,' DefParam}
* DefParam ::= {Annotation} [`inline'] Param
Expand Down Expand Up @@ -2109,10 +2090,11 @@ object Parsers {

// begin paramClause
inParens {
if (in.token == RPAREN && !prefix) Nil
val isContextual = impliedMods.is(Contextual)
if (in.token == RPAREN && !prefix && !isContextual) Nil
else {
def funArgMods(mods: Modifiers): Modifiers =
if (in.token == IMPLICIT && !mods.is(Contextual))
if (in.token == IMPLICIT && !isContextual)
funArgMods(addMod(mods, atSpan(accept(IMPLICIT)) { Mod.Implicit() }))
else if (in.token == ERASED)
funArgMods(addMod(mods, atSpan(accept(ERASED)) { Mod.Erased() }))
Expand All @@ -2139,7 +2121,7 @@ object Parsers {
ofInstance: Boolean = false): List[List[ValDef]] = {
def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] = {
val initialMods =
if (in.token == WITH) {
if (in.token == GIVEN) {
in.nextToken()
Modifiers(Contextual | Implicit)
}
Expand All @@ -2148,7 +2130,7 @@ object Parsers {
newLineOptWhenFollowedBy(LPAREN)
if (in.token == LPAREN) {
if (ofInstance && !isContextual)
syntaxError(em"parameters of instance definitions must come after `with'")
syntaxError(em"parameters of instance definitions must come after `given'")
val params = paramClause(
ofClass = ofClass,
ofCaseClass = ofCaseClass,
Expand Down Expand Up @@ -2747,7 +2729,7 @@ object Parsers {
case Typed(tree @ This(EmptyTypeIdent), tpt) =>
self = makeSelfDef(nme.WILDCARD, tpt).withSpan(first.span)
case _ =>
val ValDef(name, tpt, _) = convertToParam(first, EmptyModifiers, "self type clause")
val ValDef(name, tpt, _) = convertToParam(first, "self type clause")
if (name != nme.ERROR)
self = makeSelfDef(name, tpt).withSpan(first.span)
}
Expand Down Expand Up @@ -2836,10 +2818,10 @@ object Parsers {
stats ++= importClause()
else if (isExprIntro)
stats += expr(Location.InBlock)
else if (isDefIntro(localModifierTokens))
if (in.token == IMPLICIT || in.token == ERASED) {
else if (isDefIntro(localModifierTokens) || in.token == GIVEN) // !!!!
if (in.token == IMPLICIT || in.token == ERASED || in.token == GIVEN) {
val start = in.offset
var imods = modifiers(funArgMods)
var imods = modifiers(closureMods)
if (isBindingIntro)
stats += implicitClosure(start, Location.InBlock, imods)
else if (in.token == MATCH)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ object Scanners {
*/
protected final def fetchToken(): Unit = {
offset = charOffset - 1
name = null
(ch: @switch) match {
case ' ' | '\t' | CR | LF | FF =>
nextChar()
Expand Down
15 changes: 7 additions & 8 deletions compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ abstract class TokensCommon {
//final val SUPERTYPE = 81; enter(SUPERTYPE, ">:")
//final val HASH = 82; enter(HASH, "#")
final val AT = 83; enter(AT, "@")
//final val CARROW = 84;
//final val VIEWBOUND = 85; enter(VIEWBOUND, "<%") // TODO: deprecate
//final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate

val keywords: TokenSet

Expand Down Expand Up @@ -180,6 +179,7 @@ object Tokens extends TokensCommon {
final val ENUM = 62; enter(ENUM, "enum")
final val ERASED = 63; enter(ERASED, "erased")
final val INSTANCE = 64; enter(INSTANCE, "instance")
final val GIVEN = 65; enter(GIVEN, "given")

/** special symbols */
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
Expand All @@ -192,16 +192,15 @@ object Tokens extends TokensCommon {
final val SUBTYPE = 80; enter(SUBTYPE, "<:")
final val SUPERTYPE = 81; enter(SUPERTYPE, ">:")
final val HASH = 82; enter(HASH, "#")
final val CARROW = 84; enter(CARROW, "|=>")
final val VIEWBOUND = 85; enter(VIEWBOUND, "<%") // TODO: deprecate
final val QPAREN = 86; enter(QPAREN, "'(")
final val QBRACE = 87; enter(QBRACE, "'{")
final val QBRACKET = 88; enter(QBRACKET, "'[")
final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate
final val QPAREN = 85; enter(QPAREN, "'(")
final val QBRACE = 86; enter(QBRACE, "'{")
final val QBRACKET = 87; enter(QBRACKET, "'[")

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

final val alphaKeywords: TokenSet = tokenRange(IF, INSTANCE)
final val alphaKeywords: TokenSet = tokenRange(IF, GIVEN)
final val symbolicKeywords: TokenSet = tokenRange(USCORE, VIEWBOUND)
final val symbolicTokens: TokenSet = tokenRange(COMMA, VIEWBOUND)
final val keywords: TokenSet = alphaKeywords | symbolicKeywords
Expand Down
17 changes: 10 additions & 7 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,25 +125,25 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
else simpleNameString(tsym)
}

protected def arrowText(contextual: Boolean): Text = if (contextual) " |=> " else " => "

override def toText(tp: Type): Text = controlled {
def toTextTuple(args: List[Type]): Text =
"(" ~ argsText(args) ~ ")"

def toTextFunction(args: List[Type], contextual: Boolean, isErased: Boolean): Text =
def toTextFunction(args: List[Type], isContextual: Boolean, isErased: Boolean): Text =
changePrec(GlobalPrec) {
val argStr: Text =
if (args.length == 2 && !defn.isTupleType(args.head))
atPrec(InfixPrec) { argText(args.head) }
else
toTextTuple(args.init)
(keywordText("erased ") provided isErased) ~
argStr ~ arrowText(contextual) ~ argText(args.last)
(keywordText("given ") provided isContextual) ~
argStr ~ " => " ~ argText(args.last)
}

def toTextDependentFunction(appType: MethodType): Text =
"(" ~ paramsText(appType) ~ ")" ~ arrowText(appType.isContextual) ~ toText(appType.resultType)
def toTextDependentFunction(appType: MethodType): Text = // !!!!
(keywordText("given ") provided appType.isImplicitMethod) ~
"(" ~ paramsText(appType) ~ ") => " ~ toText(appType.resultType)

def isInfixType(tp: Type): Boolean = tp match {
case AppliedType(tycon, args) =>
Expand Down Expand Up @@ -530,7 +530,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case (arg @ ValDef(_, tpt, _)) :: Nil if tpt.isEmpty => argToText(arg)
case _ => "(" ~ Text(args map argToText, ", ") ~ ")"
}
changePrec(GlobalPrec) { argsText ~ arrowText(contextual) ~ toText(body) }
changePrec(GlobalPrec) {
(keywordText("given ") provided contextual) ~
argsText ~ " => " ~ toText(body)
}
case InfixOp(l, op, r) =>
val opPrec = parsing.precedence(op.name)
changePrec(opPrec) { toText(l) ~ " " ~ toText(op) ~ " " ~ toText(r) }
Expand Down
Loading