Skip to content

Givens without as #10538

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 10 commits into from
Dec 1, 2020
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: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -800,10 +800,12 @@ object desugar {
Nil
}
}
val classMods = if mods.is(Given) then mods &~ Given | Synthetic else mods
cpy.TypeDef(cdef: TypeDef)(
name = className,
rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1,
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)): TypeDef
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)
).withMods(classMods)
}

// install the watch on classTycon
Expand Down
53 changes: 42 additions & 11 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ object Parsers {
lookahead.nextToken()
skipParams()
skipParams()
lookahead.isIdent(nme.as)
lookahead.token == COLON || lookahead.isIdent(nme.as)

def followingIsExtension() =
val next = in.lookahead.token
Expand Down Expand Up @@ -1281,11 +1281,11 @@ object Parsers {

def possibleTemplateStart(isNew: Boolean = false): Unit =
in.observeColonEOL()
if in.token == COLONEOL then
if in.token == COLONEOL || in.token == WITHEOL then
if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
else
in.nextToken()
if in.token != INDENT then
if in.token != INDENT && in.token != LBRACE then
syntaxErrorOrIncomplete(i"indented definitions expected, ${in}")
else
newLineOptWhenFollowedBy(LBRACE)
Expand Down Expand Up @@ -3516,9 +3516,8 @@ object Parsers {
syntaxError(i"extension clause can only define methods", stat.span)
}

/** GivenDef ::= [GivenSig] Type ‘=’ Expr
* | [GivenSig] ConstrApps [TemplateBody]
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘as’
/** GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
*/
def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) {
var mods1 = addMod(mods, givenMod)
Expand All @@ -3535,23 +3534,35 @@ object Parsers {
else Nil
newLinesOpt()
val noParams = tparams.isEmpty && vparamss.isEmpty
val newSyntax = in.token == COLON
if !(name.isEmpty && noParams) then
accept(nme.as)
val parents = constrApps(commaOK = true)
if in.token == EQUALS && parents.length == 1 && parents.head.isType then
if isIdent(nme.as) then in.nextToken()
else accept(COLON)
val parents = constrApp() :: withConstrApps()
val parentsIsType = parents.length == 1 && parents.head.isType
if in.token == EQUALS && parentsIsType then
accept(EQUALS)
mods1 |= Final
if noParams && !mods.is(Inline) then
mods1 |= Lazy
ValDef(name, parents.head, subExpr())
else
DefDef(name, tparams, vparamss, parents.head, subExpr())
else if newSyntax && in.token != WITH && in.token != WITHEOL && parentsIsType then
if name.isEmpty then
syntaxError(em"anonymous given cannot be abstract")
DefDef(name, tparams, vparamss, parents.head, EmptyTree)
else
possibleTemplateStart()
val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal))
val vparamss1 = vparamss.map(_.map(vparam =>
vparam.withMods(vparam.mods &~ Param | ParamAccessor | Protected)))
val templ = templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
val constr = makeConstructor(tparams1, vparamss1)
val templ =
if newSyntax || in.token == WITHEOL || in.token == WITH then
withTemplate(constr, parents)
else
possibleTemplateStart()
templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
if tparams.isEmpty && vparamss.isEmpty then ModuleDef(name, templ)
else TypeDef(name.toTypeName, templ)
end gdef
Expand Down Expand Up @@ -3626,6 +3637,18 @@ object Parsers {
else Nil
t :: ts


/** `{`with` ConstrApp} but no EOL allowed after `with`.
*/
def withConstrApps(): List[Tree] =
if in.token == WITH then
in.observeWithEOL() // converts token to WITHEOL if at end of line
if in.token == WITH && in.lookahead.token != LBRACE then
in.nextToken()
constrApp() :: withConstrApps()
else Nil
else Nil

/** Template ::= InheritClauses [TemplateBody]
* InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
*/
Expand Down Expand Up @@ -3691,6 +3714,14 @@ object Parsers {
template(emptyConstructor)
r

/** with Template, with EOL <indent> interpreted */
def withTemplate(constr: DefDef, parents: List[Tree]): Template =
if in.token != WITHEOL then accept(WITH)
possibleTemplateStart() // consumes a WITHEOL token
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we miss else here?

Suggested change
possibleTemplateStart() // consumes a WITHEOL token
else possibleTemplateStart() // consumes a WITHEOL token

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. possibleTemplateStart expects a { or a WITHEOL.

val (self, stats) = templateBody()
Template(constr, parents, Nil, self, stats)
.withSpan(Span(constr.span.orElse(parents.head.span).start, in.lastOffset))

/* -------- STATSEQS ------------------------------------------- */

/** Create a tree representing a packaging */
Expand Down
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ object Scanners {
private[Scanners] var allowLeadingInfixOperators = true

var debugTokenStream = false
val showLookAheadOnDebug = false

val rewrite = ctx.settings.rewrite.value.isDefined
val oldSyntax = ctx.settings.oldSyntax.value
Expand Down Expand Up @@ -315,7 +316,8 @@ object Scanners {
}

final def printState() =
if debugTokenStream then print("[" + show + "]")
if debugTokenStream && (showLookAheadOnDebug || !isInstanceOf[LookaheadScanner]) then
print(s"[$show${if isInstanceOf[LookaheadScanner] then "(LA)" else ""}]")

/** Insert `token` at assumed `offset` in front of current one. */
def insert(token: Token, offset: Int) = {
Expand Down Expand Up @@ -505,12 +507,15 @@ object Scanners {
|Previous indent : $lastWidth
|Latest indent : $nextWidth"""

def observeColonEOL(): Unit =
if token == COLON then
private def switchAtEOL(testToken: Token, eolToken: Token): Unit =
if token == testToken then
lookAhead()
val atEOL = isAfterLineEnd || token == EOF
reset()
if atEOL then token = COLONEOL
if atEOL then token = eolToken

def observeColonEOL(): Unit = switchAtEOL(COLON, COLONEOL)
def observeWithEOL(): Unit = switchAtEOL(WITH, WITHEOL)

def observeIndented(): Unit =
if indentSyntax && isNewLine then
Expand Down
23 changes: 12 additions & 11 deletions compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ abstract class TokensCommon {
def isKeyword(token: Token): Boolean = keywords contains token

/** parentheses */
final val LPAREN = 90; enter(LPAREN, "'('")
final val RPAREN = 91; enter(RPAREN, "')'")
final val LBRACKET = 92; enter(LBRACKET, "'['")
final val RBRACKET = 93; enter(RBRACKET, "']'")
final val LBRACE = 94; enter(LBRACE, "'{'")
final val RBRACE = 95; enter(RBRACE, "'}'")
final val INDENT = 96; enter(INDENT, "indent")
final val OUTDENT = 97; enter(OUTDENT, "unindent")
final val LPAREN = 91; enter(LPAREN, "'('")
final val RPAREN = 92; enter(RPAREN, "')'")
final val LBRACKET = 93; enter(LBRACKET, "'['")
final val RBRACKET = 94; enter(RBRACKET, "']'")
final val LBRACE = 95; enter(LBRACE, "'{'")
final val RBRACE = 96; enter(RBRACE, "'}'")
final val INDENT = 97; enter(INDENT, "indent")
final val OUTDENT = 98; enter(OUTDENT, "unindent")

final val firstParen = LPAREN
final val lastParen = OUTDENT
Expand Down Expand Up @@ -204,10 +204,11 @@ object Tokens extends TokensCommon {
final val QUOTE = 87; enter(QUOTE, "'")

final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
final val SELFARROW = 89; enter(SELFARROW, "=>") // reclassified ARROW following self-type
final val WITHEOL = 89; enter(WITHEOL, "with", "with at eol")
final val SELFARROW = 90; enter(SELFARROW, "=>") // reclassified ARROW following self-type

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

final val alphaKeywords: TokenSet = tokenRange(IF, MACRO)
final val symbolicKeywords: TokenSet = tokenRange(USCORE, CTXARROW)
Expand Down Expand Up @@ -276,7 +277,7 @@ object Tokens extends TokensCommon {
final val closingRegionTokens = BitSet(RBRACE, RPAREN, RBRACKET, CASE) | statCtdTokens

final val canStartIndentTokens: BitSet =
statCtdTokens | BitSet(COLONEOL, EQUALS, ARROW, LARROW, WHILE, TRY, FOR, IF)
statCtdTokens | BitSet(COLONEOL, WITHEOL, EQUALS, ARROW, LARROW, WHILE, TRY, FOR, IF)
// `if` is excluded because it often comes after `else` which makes for awkward indentation rules TODO: try to do without the exception

/** Faced with the choice between a type and a formal parameter, the following
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ object Checking {
fail(ParamsNoInline(sym.owner))
if sym.isInlineMethod && !sym.is(Deferred) && sym.allOverriddenSymbols.nonEmpty then
checkInlineOverrideParameters(sym)
if (sym.isOneOf(GivenOrImplicit)) {
if (sym.is(Implicit)) {
if (sym.owner.is(Package))
fail(TopLevelCantBeImplicit(sym))
if (sym.isType)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class Namer { typer: Typer =>

tree match {
case tree: TypeDef if tree.isClassDef =>
val flags = checkFlags(tree.mods.flags &~ GivenOrImplicit)
val flags = checkFlags(tree.mods.flags &~ Implicit)
val name = checkNoConflict(tree.name, flags.is(Private), tree.span).asTypeName
val cls =
createOrRefine[ClassSymbol](tree, name, flags, ctx.owner,
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,9 @@ object RefChecks {
def prelude = (
if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible"
else if (mustBeMixin) s"$clazz needs to be a mixin"
else s"$clazz needs to be abstract") + ", since"
else if clazz.is(Synthetic) then "instance cannot be created"
else s"$clazz needs to be abstract"
) + ", since"

if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg)
else abstractErrors += msg
Expand Down
7 changes: 4 additions & 3 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,11 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[
| [SimpleExpr ‘.’] id ‘=’ Expr Assign(expr, expr)
| SimpleExpr1 ArgumentExprs ‘=’ Expr Assign(expr, expr)
| PostfixExpr [Ascription]
| StructuralInstance
| ‘inline’ InfixExpr MatchClause
Ascription ::= ‘:’ InfixType Typed(expr, tp)
| ‘:’ Annotation {Annotation} Typed(expr, Annotated(EmptyTree, annot)*)
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} ‘with’ TemplateBody New templ
Catches ::= ‘catch’ (Expr | ExprCaseClause)
PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op)
InfixExpr ::= PrefixExpr
Expand Down Expand Up @@ -396,9 +398,8 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
ConstrMods ::= {Annotation} [AccessModifier]
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template)
GivenDef ::= [GivenSig] Type ‘=’ Expr
| [GivenSig] ConstrApps [TemplateBody]
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘as’ -- one of `id`, `DefParamClause`, `UsingParamClause` must appear
GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present
Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’
{UsingParamClause}] ExtMethods
ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/changed-features/implicit-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ no longer applies.
**3.** Package prefixes no longer contribute to the implicit search scope of a type. Example:
```scala
package p
given a as A
given a: A = A()

object o {
given b as B
given b: B = B()
type C
}
```
Expand Down
22 changes: 8 additions & 14 deletions docs/docs/reference/changed-features/numeric-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,38 +133,32 @@ should produce the `BigFloat` number `BigFloat(-123, 997)`:
The companion object of `BigFloat` defines an `apply` constructor method to construct a `BigFloat`
from a `digits` string. Here is a possible implementation:
```scala
object BigFloat {
object BigFloat:
import scala.util.FromDigits

def apply(digits: String): BigFloat = {
val (mantissaDigits, givenExponent) = digits.toUpperCase.split('E') match {
def apply(digits: String): BigFloat =
val (mantissaDigits, givenExponent) = digits.toUpperCase.split('E') match
case Array(mantissaDigits, edigits) =>
val expo =
try FromDigits.intFromDigits(edigits)
catch {
case ex: FromDigits.NumberTooLarge =>
throw FromDigits.NumberTooLarge(s"exponent too large: $edigits")
}
catch case ex: FromDigits.NumberTooLarge =>
throw FromDigits.NumberTooLarge(s"exponent too large: $edigits")
(mantissaDigits, expo)
case Array(mantissaDigits) =>
(mantissaDigits, 0)
}
val (intPart, exponent) = mantissaDigits.split('.') match {
val (intPart, exponent) = mantissaDigits.split('.') match
case Array(intPart, decimalPart) =>
(intPart ++ decimalPart, givenExponent - decimalPart.length)
case Array(intPart) =>
(intPart, givenExponent)
}
BigFloat(BigInt(intPart), exponent)
}
```
To accept `BigFloat` literals, all that's needed in addition is a `given` instance of type
`FromDigits.Floating[BigFloat]`:
```scala
given FromDigits as FromDigits.Floating[BigFloat] {
given FromDigits: FromDigits.Floating[BigFloat] with
def fromDigits(digits: String) = apply(digits)
}
} // end BigFloat
end BigFloat
```
Note that the `apply` method does not check the format of the `digits` argument. It is
assumed that only valid arguments are passed. For calls coming from the compiler
Expand Down
12 changes: 5 additions & 7 deletions docs/docs/reference/contextual/by-name-context-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ trait Codec[T] {
def write(x: T): Unit
}

given intCodec as Codec[Int] = ???
given intCodec: Codec[Int] = ???

given optionCodec[T](using ev: => Codec[T]) as Codec[Option[T]] {
def write(xo: Option[T]) = xo match {
given optionCodec[T](using ev: => Codec[T]): Codec[Option[T]] with
def write(xo: Option[T]) = xo match
case Some(x) => ev.write(x)
case None =>
}
}

val s = summon[Codec[Option[Int]]]

Expand All @@ -36,7 +34,7 @@ The precise steps for synthesizing an argument for a by-name context parameter o
1. Create a new given of type `T`:

```scala
given lv as T = ???
given lv: T = ???
```
where `lv` is an arbitrary fresh name.

Expand All @@ -46,7 +44,7 @@ The precise steps for synthesizing an argument for a by-name context parameter o


```scala
{ given lv as T = E; lv }
{ given lv: T = E; lv }
```

Otherwise, return `E` unchanged.
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/contextual/context-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Context functions are written using `?=>` as the "arrow" sign.
They are applied to synthesized arguments, in
the same way methods with context parameters are applied. For instance:
```scala
given ec as ExecutionContext = ...
given ec: ExecutionContext = ...

def f(x: Int): ExecutionContext ?=> Int = ...

Expand Down Expand Up @@ -86,13 +86,13 @@ with context function types as parameters to avoid the plumbing boilerplate
that would otherwise be necessary.
```scala
def table(init: Table ?=> Unit) = {
given t as Table // note the use of a creator application; same as: given t as Table = new Table
given t: Table = Table()
init
t
}

def row(init: Row ?=> Unit)(using t: Table) = {
given r as Row
given r: Row = Row()
init
t.add(r)
}
Expand Down
Loading