Skip to content

Change erased syntax #6278

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

Merged
merged 4 commits into from
Apr 11, 2019
Merged
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
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 @@ -106,13 +106,13 @@ class Definitions {
* ErasedFunctionN traits follow this template:
*
* trait ErasedFunctionN[T0,...,T{N-1}, R] extends Object {
* def apply(erased $x0: T0, ..., $x{N_1}: T{N-1}): R
* def apply erased ($x0: T0, ..., $x{N_1}: T{N-1}): R
* }
*
* ErasedImplicitFunctionN traits follow this template:
*
* trait ErasedImplicitFunctionN[T0,...,T{N-1}, R] extends Object {
* def apply given (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
6 changes: 2 additions & 4 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -432,15 +432,14 @@ object TastyFormat {
final val CONTEXTUALMETHODtype = 182
final val ERASEDCONTEXTUALMETHODtype = 183
final val IMPLICITMETHODtype = 184
final val ERASEDIMPLICITMETHODtype = 185

final val MATCHtype = 190
final val MATCHtpt = 191

def methodType(isContextual: Boolean, isImplicit: Boolean, isErased: Boolean): Int = {
val implicitOffset =
if (isContextual) 2
else if (isImplicit) 4
else if (isImplicit) { assert(!isErased); 4 }
else 0
val erasedOffset = if (isErased) 1 else 0
METHODtype + erasedOffset + implicitOffset
Expand Down Expand Up @@ -650,7 +649,6 @@ object TastyFormat {
case CONTEXTUALMETHODtype => "CONTEXTUALMETHODtype"
case ERASEDCONTEXTUALMETHODtype => "ERASEDCONTEXTUALMETHODtype"
case IMPLICITMETHODtype => "IMPLICITMETHODtype"
case ERASEDIMPLICITMETHODtype => "ERASEDIMPLICITMETHODtype"
case TYPELAMBDAtype => "TYPELAMBDAtype"
case LAMBDAtpt => "LAMBDAtpt"
case MATCHtype => "MATCHtype"
Expand All @@ -672,7 +670,7 @@ object TastyFormat {
case POLYtype | TYPELAMBDAtype |
METHODtype | ERASEDMETHODtype |
CONTEXTUALMETHODtype | ERASEDCONTEXTUALMETHODtype |
IMPLICITMETHODtype | ERASEDIMPLICITMETHODtype => -1
IMPLICITMETHODtype => -1
case _ => 0
}
}
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) {
printNat(); printTrees()
case METHODtype | ERASEDMETHODtype |
CONTEXTUALMETHODtype | ERASEDCONTEXTUALMETHODtype |
IMPLICITMETHODtype | ERASEDIMPLICITMETHODtype |
POLYtype | TYPELAMBDAtype =>
IMPLICITMETHODtype | POLYtype | TYPELAMBDAtype =>
printTree()
until(end) { printName(); printTree() }
case PARAMtype =>
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,6 @@ class TreeUnpickler(reader: TastyReader,
readMethodic(ErasedContextualMethodType, _.toTermName)
case IMPLICITMETHODtype =>
readMethodic(ImplicitMethodType, _.toTermName)
case ERASEDIMPLICITMETHODtype =>
readMethodic(ErasedImplicitMethodType, _.toTermName)
case TYPELAMBDAtype =>
readMethodic(HKTypeLambda, _.toTypeName)
case PARAMtype =>
Expand Down
50 changes: 22 additions & 28 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1210,7 +1210,7 @@ object Parsers {

def expr(location: Location.Value): Tree = {
val start = in.offset
if (in.token == IMPLICIT || in.token == ERASED || in.token == GIVEN) {
if (closureMods.contains(in.token)) {
val imods = modifiers(closureMods)
if (in.token == MATCH) implicitMatch(start, imods)
else implicitClosure(start, location, imods)
Expand Down Expand Up @@ -1994,11 +1994,9 @@ object Parsers {
normalize(loop(start))
}

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

Expand Down Expand Up @@ -2083,11 +2081,12 @@ object Parsers {
def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] =
if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil

/** ClsParamClause ::= [nl | ‘with’] `(' [FunArgMods] [ClsParams] ')'
/** ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’
* | ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | ContextTypes)
* ClsParams ::= ClsParam {`' ClsParam}
* ClsParam ::= {Annotation} [{ParamModifier} (`val' | `var') | `inline'] Param
* DefParamClause ::= [nl] `(' [FunArgMods] [DefParams] ')' | InferParamClause
* InferParamClause ::= ‘given’ (‘(’ DefParams ‘)’ | ContextTypes)
* DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause
* GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | ContextTypes)
* ContextTypes ::= RefinedType {`,' RefinedType}
* DefParams ::= DefParam {`,' DefParam}
* DefParam ::= {Annotation} [`inline'] Param
Expand Down Expand Up @@ -2158,17 +2157,10 @@ object Parsers {

// begin paramClause
inParens {
val isContextual = impliedMods.is(Given)
if (in.token == RPAREN && !prefix && !isContextual) Nil
if (in.token == RPAREN && !prefix && !impliedMods.is(Given)) Nil
else {
def funArgMods(mods: Modifiers): Modifiers =
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() }))
else mods

impliedMods = funArgMods(impliedMods)
if (in.token == IMPLICIT && !impliedMods.is(Given | Erased))
impliedMods = addMod(impliedMods, atSpan(accept(IMPLICIT)) { Mod.Implicit() })
val clause =
if (prefix) param() :: Nil
else commaSeparated(() => param())
Expand All @@ -2178,22 +2170,24 @@ object Parsers {
}
}

/** ClsParamClauses ::= {ClsParamClause}
* DefParamClauses ::= {DefParamClause}
* InferParamClauses ::= {InferParamClause}
/** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
* DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
*
* @return The parameter definitions
*/
def paramClauses(ofClass: Boolean = false,
ofCaseClass: Boolean = false,
ofInstance: Boolean = false): List[List[ValDef]] = {
def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] = {
val initialMods =
if (in.token == GIVEN) {
in.nextToken()
Modifiers(Given | Implicit)
}
else EmptyModifiers
var initialMods = EmptyModifiers
if (in.token == GIVEN) {
in.nextToken()
initialMods |= Given | Implicit
}
if (in.token == ERASED) {
in.nextToken()
initialMods |= Erased
}
val isContextual = initialMods.is(Given)
newLineOptWhenFollowedBy(LPAREN)
if (in.token == LPAREN) {
Expand Down Expand Up @@ -2617,7 +2611,7 @@ object Parsers {
}

/** InstanceDef ::= [id] InstanceParams InstanceBody
* InstanceParams ::= [DefTypeParamClause] {InferParamClause}
* InstanceParams ::= [DefTypeParamClause] {GivenParamClause}
* InstanceBody ::= [‘of’ ConstrApp {‘,’ ConstrApp }] [TemplateBody]
* | ‘of’ Type ‘=’ Expr
*/
Expand Down Expand Up @@ -2906,7 +2900,7 @@ object Parsers {
else if (isExprIntro)
stats += expr(Location.InBlock)
else if (isDefIntro(localModifierTokens))
if (in.token == IMPLICIT || in.token == ERASED || in.token == GIVEN) {
if (closureMods.contains(in.token)) {
val start = in.offset
var imods = modifiers(closureMods)
if (isBindingIntro)
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,10 @@ class PlainPrinter(_ctx: Context) extends Printer {
"<noprefix>"
case tp: MethodType =>
changePrec(GlobalPrec) {
(if (tp.isContextual) " given " else "") ~
("(" + (if (tp.isErasedMethod) "erased " else "")
+ (if (tp.isImplicitMethod && !tp.isContextual) "implicit " else "")
) ~ paramsText(tp) ~
(if (tp.isContextual) " given" else "") ~
(if (tp.isErasedMethod) " erased" else "") ~~
("(" + (if (tp.isImplicitMethod && !tp.isContextual) "implicit " else "")) ~
paramsText(tp) ~
(if (tp.resultType.isInstanceOf[MethodType]) ")" else "): ") ~
toText(tp.resultType)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
atPrec(InfixPrec) { argText(args.head) }
else
toTextTuple(args.init)
(keywordText("erased ") provided isErased) ~
(keywordText("given ") provided isContextual) ~
(keywordText("erased ") provided isErased) ~
argStr ~ " => " ~ argText(args.last)
}

def toTextDependentFunction(appType: MethodType): Text =
(keywordText("erased ") provided appType.isErasedMethod) ~
(keywordText("given ") provided appType.isImplicitMethod) ~
(keywordText("erased ") provided appType.isErasedMethod) ~
"(" ~ paramsText(appType) ~ ") => " ~ toText(appType.resultType)

def isInfixType(tp: Type): Boolean = tp match {
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1647,13 +1647,15 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util.
/** Intersection of the two flag sets */
def Flags_and(self: Flags)(that: Flags): Flags = self & that

def Flags_EmptyFlags: Flags = core.Flags.EmptyFlags
def Flags_Private: Flags = core.Flags.Private
def Flags_Protected: Flags = core.Flags.Protected
def Flags_Abstract: Flags = core.Flags.Abstract
def Flags_Final: Flags = core.Flags.Final
def Flags_Sealed: Flags = core.Flags.Sealed
def Flags_Case: Flags = core.Flags.Case
def Flags_Implicit: Flags = core.Flags.Implicit
def Flags_Given: Flags = core.Flags.Given
def Flags_Implied: Flags = core.Flags.Implied
def Flags_Erased: Flags = core.Flags.Erased
def Flags_Lazy: Flags = core.Flags.Lazy
Expand Down
4 changes: 2 additions & 2 deletions compiler/test-resources/repl/erased
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scala> def f(erased a: Int): Int = ???
def f(erased a: Int): Int
scala> def f erased (a: Int): Int = ???
def f erased (a: Int): Int
4 changes: 2 additions & 2 deletions compiler/test-resources/repl/erased-implicit
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
scala> def f(erased implicit a: Int): Int = ???
def f(erased implicit a: Int): Int
scala> def f given erased (a: Int): Int = ???
def f given erased (a: Int): Int
15 changes: 7 additions & 8 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,22 +290,21 @@ HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’
HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (Id[HkTypeParamClause] | ‘_’)
SubtypeBounds

ClsParamClauses ::= {ClsParamClause}
ClsParamClause ::= [nl] (’ [[FunArgMods] ClsParams] ‘)’
| ‘given’ (‘(’ ([[FunArgMods] ClsParams] ‘)’ | ContextTypes)
ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
ClsParamClause ::= [nl] [‘erased’] ‘(’ [ClsParams] ‘)’
| ‘given’ [‘erased’] (‘(’ ClsParams ‘)’ | ContextTypes)
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] ‘(’ [FunArgMods] DefParams ‘)’]
DefParamClause ::= [nl] (’ [DefParams] ‘)’ | InferParamClause
InferParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | ContextTypes)
DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
DefParamClause ::= [nl] [‘erased’] ‘(’ [DefParams] ‘)’ | GivenParamClause
GivenParamClause ::= ‘given’ [‘erased’] (‘(’ DefParams ‘)’ | ContextTypes)
DefParams ::= DefParam {‘,’ DefParam}
DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id.
ContextTypes ::= RefinedType {‘,’ RefinedType}
FunArgMods ::= { ‘implicit’ | ‘erased’ }
ClosureMods ::= { ‘implicit’ | ‘erased’ | ‘given’}
```

Expand Down Expand Up @@ -379,7 +378,7 @@ ConstrMods ::= {Annotation} [AccessModifier]
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template)
InstanceDef ::= [id] InstanceParams InstanceBody
InstanceParams ::= [DefTypeParamClause] {InferParamClause}
InstanceParams ::= [DefTypeParamClause] {GivenParamClause}
InstanceBody ::= [‘of’ ConstrApp {‘,’ ConstrApp }] [TemplateBody]
| ‘of’ Type ‘=’ Expr
Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats)
Expand Down
38 changes: 19 additions & 19 deletions docs/docs/reference/other-new-features/erased-terms.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ present in some form in the generated code to be able to do separate compilation

How to define erased terms?
-------------------------------
Parameters of methods and functions can be declared as erased, placing `erased` at the start of the parameter list (like `implicit`).
Parameters of methods and functions can be declared as erased, placing `erased` in front of a parameter list (like `given`).

```scala
def methodWithErasedEv(erased ev: Ev): Int = 42
def methodWithErasedEv erased (ev: Ev): Int = 42

val lambdaWithErasedEv: erased Ev => Int =
erased (ev: Ev) => 42
Expand All @@ -50,10 +50,10 @@ val lambdaWithErasedEv: erased Ev => Int =
`erased` parameters will not be usable for computations, though they can be used as arguments to other `erased` parameters.

```scala
def methodWithErasedInt1(erased i: Int): Int =
def methodWithErasedInt1 erased (i: Int): Int =
i + 42 // ERROR: can not use i

def methodWithErasedInt2(erased i: Int): Int =
def methodWithErasedInt2 erased (i: Int): Int =
methodWithErasedInt1(i) // OK
```

Expand All @@ -70,7 +70,7 @@ As `erased` are guaranteed not to be used in computations, they can and will be

```scala
// becomes def methodWithErasedEv(): Int at runtime
def methodWithErasedEv(erased ev: Ev): Int = ...
def methodWithErasedEv erased (ev: Ev): Int = ...

def evidence1: Ev = ...
erased def erasedEvidence2: Ev = ... // does not exist at runtime
Expand All @@ -83,13 +83,13 @@ methodWithErasedEv(evidence1)
State machine with erased evidence example
------------------------------------------
The following example is an extended implementation of a simple state machine which can be in a state `On` or `Off`.
The machine can change state from `Off` to `On` with `turnedOn` only if it is currently `Off`,
The machine can change state from `Off` to `On` with `turnedOn` only if it is currently `Off`,
conversely from `On` to `Off` with `turnedOff` only if it is currently `On`. These last constraint are
captured with the `IsOff[S]` and `IsOn[S]` implicit evidence only exist for `IsOff[Off]` and `IsOn[On]`.
For example, not allowing calling `turnedOff` on in an `Off` state as we would require an evidence `IsOn[Off]`
captured with the `IsOff[S]` and `IsOn[S]` implicit evidence only exist for `IsOff[Off]` and `IsOn[On]`.
For example, not allowing calling `turnedOff` on in an `Off` state as we would require an evidence `IsOn[Off]`
that will not be found.

As the implicit evidences of `turnedOn` and `turnedOff` are not used in the bodies of those functions
As the implicit evidences of `turnedOn` and `turnedOff` are not used in the bodies of those functions
we can mark them as `erased`. This will remove the evidence parameters at runtime, but we would still
evaluate the `isOn` and `isOff` implicits that were found as arguments.
As `isOn` and `isOff` are not used except as `erased` arguments, we can mark them as `erased`, hence
Expand Down Expand Up @@ -117,9 +117,9 @@ object IsOn {
}

class Machine[S <: State] private {
// ev will disapear from both functions
def turnedOn(implicit erased ev: IsOff[S]): Machine[On] = new Machine[On]
def turnedOff(implicit erased ev: IsOn[S]): Machine[Off] = new Machine[Off]
// ev will disappear from both functions
def turnedOn given erased (ev: IsOff[S]): Machine[On] = new Machine[On]
def turnedOff given erased (ev: IsOn[S]): Machine[Off] = new Machine[Off]
}

object Machine {
Expand Down Expand Up @@ -156,12 +156,12 @@ Rules
erased val x = ...
erased def f = ...

def g(erased x: Int) = ...
def g erased (x: Int) = ...

(erased x: Int) => ...
erased (x: Int) => ...
def h(x: erased Int => Int) = ...

class K(erased x: Int) { ... }
class K erased (x: Int) { ... }
```


Expand All @@ -182,14 +182,14 @@ Rules

4. Eta expansion

if `def f(erased x: T): U` then `f: (erased T) => U`.
if `def f erased (x: T): U` then `f: erased (T) => U`.


5. Erasure Semantics
* All `erased` parameters are removed from the function
* All argument to `erased` parameters are not passed to the function
* All `erased` definitions are removed
* All `(erased T1, T2, ..., TN) => R` and `(given erased T1, T2, ..., TN) => R` become `() => R`
* All ` erased (T1, T2, ..., TN) => R` and `(given erased T1, T2, ..., TN) => R` become `() => R`


6. Overloading
Expand All @@ -198,6 +198,6 @@ Rules


7. Overriding
* Member definitions overidding each other must both be `erased` or not be `erased`
* `def foo(x: T): U` cannot be overridden by `def foo(erased x: T): U` an viceversa
* Member definitions overriding each other must both be `erased` or not be `erased`
* `def foo(x: T): U` cannot be overridden by `def foo erased (x: T): U` an vice-versa

Loading