Skip to content

Trial: given as instead of delegate for #6773

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 2 commits into from
Jul 4, 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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,7 @@ object desugar {
*/
def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = {
var name = mdef.name
if (name.isEmpty) name = name.likeSpaced(s"${inventName(impl)}_instance".toTermName)
if (name.isEmpty) name = name.likeSpaced(s"${inventName(impl)}_given".toTermName)
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
def kind = if (name.isTypeName) "class" else "object"
ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos)
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package dotty.tools.dotc
package core

import language.implicitConversions

object Flags {

object opaques {
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ object StdNames {
val array_length : N = "array_length"
val array_update : N = "array_update"
val arraycopy: N = "arraycopy"
val as: N = "as"
val asTerm: N = "asTerm"
val asModule: N = "asModule"
val asMethod: N = "asMethod"
Expand Down
146 changes: 101 additions & 45 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,16 @@ object Parsers {
/** Is current token a hard or soft modifier (in modifier position or not)? */
def isModifier: Boolean = modifierTokens.contains(in.token) || in.isSoftModifier

def isBindingIntro: Boolean =
canStartBindingTokens.contains(in.token) &&
!in.isSoftModifierInModifierPosition
def isBindingIntro: Boolean = {
in.token match {
case USCORE | LPAREN => true
case IDENTIFIER | BACKQUOTED_IDENT => in.lookaheadIn(BitSet(COLON, ARROW))
case _ => false
}
} && !in.isSoftModifierInModifierPosition

def isExprIntro: Boolean =
if (in.token == IMPLIED) in.lookaheadIn(BitSet(MATCH))
if (in.token == IMPLIED || in.token == GIVEN) in.lookaheadIn(BitSet(MATCH))
else (canStartExpressionTokens.contains(in.token) && !in.isSoftModifierInModifierPosition)

def isDefIntro(allowedMods: BitSet, excludedSoftModifiers: Set[TermName] = Set.empty): Boolean =
Expand Down Expand Up @@ -1274,7 +1278,7 @@ object Parsers {
* | SimpleExpr1 ArgumentExprs `=' Expr
* | Expr2
* | [‘inline’] Expr2 `match' `{' CaseClauses `}'
* | `delegate' `match' `{' ImplicitCaseClauses `}'
* | `given' `match' `{' ImplicitCaseClauses `}'
* Bindings ::= `(' [Binding {`,' Binding}] `)'
* Binding ::= (id | `_') [`:' Type]
* Expr2 ::= PostfixExpr [Ascription]
Expand All @@ -1293,7 +1297,7 @@ object Parsers {
if (in.token == MATCH) impliedMatch(start, imods)
else implicitClosure(start, location, imods)
}
else if(in.token == IMPLIED) {
else if (in.token == IMPLIED || in.token == GIVEN) {
in.nextToken()
if (in.token == MATCH)
impliedMatch(start, EmptyModifiers)
Expand Down Expand Up @@ -1478,7 +1482,7 @@ object Parsers {
*/
def impliedMatch(start: Int, imods: Modifiers) = {
def markFirstIllegal(mods: List[Mod]) = mods match {
case mod :: _ => syntaxError(em"illegal modifier for delegate match", mod.span)
case mod :: _ => syntaxError(em"illegal modifier for given match", mod.span)
case _ =>
}
imods.mods match {
Expand All @@ -1493,7 +1497,7 @@ object Parsers {
case pat => isVarPattern(pat)
}
if (!isImplicitPattern(pat))
syntaxError(em"not a legal pattern for a delegate match", pat.span)
syntaxError(em"not a legal pattern for a given match", pat.span)
}
result
}
Expand Down Expand Up @@ -2303,8 +2307,59 @@ object Parsers {
def paramClauses(ofClass: Boolean = false,
ofCaseClass: Boolean = false,
ofInstance: Boolean = false): List[List[ValDef]] = {

def followingIsParamClause: Boolean = {
val lookahead = in.lookaheadScanner
lookahead.nextToken()
paramIntroTokens.contains(lookahead.token) && {
lookahead.token != IDENTIFIER ||
lookahead.name == nme.inline || {
lookahead.nextToken()
lookahead.token == COLON
}
}
}

/** For given instance definitions we have a disambiguation problem:
* given A as B
* given C ...
* Is the second line a parameter `given C` for the first `given` definition, or is it
* a second `given` definition? We only know if we find a `for` or `as` in `...`
* The same problem arises for
* class A
* given C ...
* For method definitions we do not have this problem since a parameter clause
* in a method definition is always followed by something else. So in
* def m(...)
* given C ...
* we know that `given` must start a parameter list. It cannot be a new given` definition.
*/
def followingIsInstanceDef =
(ofClass || ofInstance) && {
val lookahead = in.lookaheadScanner // skips newline on startup
lookahead.nextToken() // skip the `given`
if (lookahead.token == IDENTIFIER || lookahead.token == BACKQUOTED_IDENT) {
lookahead.nextToken()
if (lookahead.token == LBRACKET) {
lookahead.nextToken()
var openBrackets = 1
while (openBrackets > 0 && lookahead.token != EOF) {
if (lookahead.token == LBRACKET) openBrackets += 1
else if (lookahead.token == RBRACKET) openBrackets -= 1
lookahead.nextToken()
}
}
}
lookahead.token == FOR ||
lookahead.token == IDENTIFIER && lookahead.name == nme.as
}

def recur(firstClause: Boolean, nparams: Int, contextualOnly: Boolean): List[List[ValDef]] = {
var initialMods = EmptyModifiers
val isNewLine = in.token == NEWLINE
newLineOptWhenFollowedBy(LPAREN)
if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsInstanceDef)
in.nextToken()
if (in.token == GIVEN) {
in.nextToken()
initialMods |= Given
Expand All @@ -2313,22 +2368,10 @@ object Parsers {
in.nextToken()
initialMods |= Erased
}
val isContextual = initialMods.is(Given)
val isGiven = initialMods.is(Given)
newLineOptWhenFollowedBy(LPAREN)
def isParamClause: Boolean =
!isContextual || {
val lookahead = in.lookaheadScanner
lookahead.nextToken()
paramIntroTokens.contains(lookahead.token) && {
lookahead.token != IDENTIFIER ||
lookahead.name == nme.inline || {
lookahead.nextToken()
lookahead.token == COLON
}
}
}
if (in.token == LPAREN && isParamClause) {
if (contextualOnly && !isContextual)
if (in.token == LPAREN && (!isGiven || followingIsParamClause)) {
if (contextualOnly && !isGiven)
if (ofInstance) syntaxError(em"parameters of instance definitions must come after `given'")
else syntaxError(em"normal parameters cannot come after `given' clauses")
val params = paramClause(
Expand All @@ -2337,15 +2380,15 @@ object Parsers {
firstClause = firstClause,
initialMods = initialMods)
val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit)
params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isContextual))
params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isGiven))
}
else if (isContextual) {
else if (isGiven) {
val tps = commaSeparated(() => annotType())
var counter = nparams
def nextIdx = { counter += 1; counter }
val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param
val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given))
params :: recur(firstClause = false, nparams + params.length, isContextual)
params :: recur(firstClause = false, nparams + params.length, isGiven)
}
else Nil
}
Expand All @@ -2359,12 +2402,12 @@ object Parsers {

type ImportConstr = (Boolean, Tree, List[Tree]) => Tree

/** Import ::= import [delegate] [ImportExpr {`,' ImportExpr}
* Export ::= export [delegate] [ImportExpr {`,' ImportExpr}
/** Import ::= `import' [`given'] [ImportExpr {`,' ImportExpr}
* Export ::= `export' [`given'] [ImportExpr {`,' ImportExpr}
*/
def importClause(leading: Token, mkTree: ImportConstr): List[Tree] = {
val offset = accept(leading)
val importDelegate = in.token == IMPLIED
val importDelegate = in.token == IMPLIED || in.token == GIVEN
if (importDelegate) in.nextToken()
commaSeparated(importExpr(importDelegate, mkTree)) match {
case t :: rest =>
Expand All @@ -2383,15 +2426,21 @@ object Parsers {

/** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’
* FinalSelector ::= ImportSelector
* | ‘_’
* | ‘for’ InfixType {‘,’ InfixType}
* | ‘_’ [‘:’ Type]
*/
def importSelectors(): List[Tree] = in.token match {
case USCORE =>
wildcardIdent() :: Nil
atSpan(in.skipToken()) {
val id = Ident(nme.WILDCARD)
if (in.token == COLON) {
in.nextToken()
TypeBoundsTree(EmptyTree, typ())
}
else id
} :: Nil
case FOR =>
if (!importDelegate)
syntaxError(em"`for` qualifier only allowed in `import delegate`")
syntaxError(em"`for` qualifier only allowed in `import given`")
atSpan(in.skipToken()) {
var t = infixType()
while (in.token == COMMA) {
Expand Down Expand Up @@ -2695,7 +2744,7 @@ object Parsers {
/** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
* | [‘case’] ‘object’ ObjectDef
* | ‘enum’ EnumDef
* | ‘instance’ InstanceDef
* | ‘given’ GivenDef
*/
def tmplDef(start: Int, mods: Modifiers): Tree = {
in.token match {
Expand All @@ -2711,8 +2760,8 @@ object Parsers {
objectDef(start, posMods(start, mods | Case | Module))
case ENUM =>
enumDef(start, posMods(start, mods | Enum))
case IMPLIED =>
instanceDef(start, mods, atSpan(in.skipToken()) { Mod.Delegate() })
case IMPLIED | GIVEN =>
instanceDef(in.token == GIVEN, start, mods, atSpan(in.skipToken()) { Mod.Delegate() })
case _ =>
syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition())
EmptyTree
Expand Down Expand Up @@ -2804,17 +2853,16 @@ object Parsers {
Template(constr, parents, Nil, EmptyValDef, Nil)
}

/** InstanceDef ::= [id] [DefTypeParamClause] InstanceBody
* InstanceParams ::= [DefTypeParamClause] {GivenParamClause}
* InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
* | ‘for’ Type {GivenParamClause} ‘=’ Expr
/** GivenDef ::= [id] [DefTypeParamClause] GivenBody
* GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
* | ‘as’ Type {GivenParamClause} ‘=’ Expr
*/
def instanceDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
def instanceDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
var mods1 = addMod(mods, instanceMod)
val name = if (isIdent) ident() else EmptyTermName
val name = if (isIdent && (!newStyle || in.name != nme.as)) ident() else EmptyTermName
val tparams = typeParamClauseOpt(ParamOwner.Def)
val parents =
if (in.token == FOR) {
if (!newStyle && in.token == FOR || isIdent(nme.as)) { // for the moment, accept both `given for` and `given as`
in.nextToken()
tokenSeparated(COMMA, constrApp)
}
Expand Down Expand Up @@ -3121,8 +3169,16 @@ object Parsers {
setLastStatOffset()
if (in.token == IMPORT)
stats ++= importClause(IMPORT, Import)
else if (in.token == GIVEN)
stats += implicitClosure(in.offset, Location.InBlock, modifiers(closureMods))
else if (in.token == GIVEN) {
val start = in.offset
val mods = modifiers(closureMods)
mods.mods match {
case givenMod :: Nil if !isBindingIntro =>
stats += instanceDef(true, start, EmptyModifiers, Mod.Delegate().withSpan(givenMod.span))
case _ =>
stats += implicitClosure(in.offset, Location.InBlock, mods)
}
}
else if (isExprIntro)
stats += expr(Location.InBlock)
else if (isDefIntro(localModifierTokens, excludedSoftModifiers = Set(nme.`opaque`)))
Expand Down
6 changes: 2 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,14 @@ object Tokens extends TokensCommon {
USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, QUOTEID, XMLSTART)

final val canStartExpressionTokens: TokenSet = atomicExprTokens | BitSet(
LBRACE, LPAREN, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, IMPLIED)
LBRACE, LPAREN, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, IMPLIED, GIVEN)

final val canStartTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet(
THIS, SUPER, USCORE, LPAREN, AT)

final val canStartBindingTokens: TokenSet = identifierTokens | BitSet(USCORE, LPAREN)

final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT)

final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, IMPLIED)
final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, IMPLIED, GIVEN)

final val defIntroTokens: TokenSet = templateIntroTokens | dclIntroTokens

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 @@ -319,7 +319,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case _ => tree
}

def importText(deleg: Boolean, expr: Tree, selectors: List[Tree]) = {
def importText(givenOnly: Boolean, expr: Tree, selectors: List[Tree]) = {
def selectorText(sel: Tree): Text = sel match {
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
case _: Ident => toTextGlobal(sel)
Expand All @@ -329,7 +329,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case id :: Nil => toText(id)
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
}
(keywordText("delegate ") provided deleg) ~
(keywordText("given ") provided givenOnly) ~
toTextLocal(expr) ~ "." ~ selectorsText
}

Expand Down
18 changes: 9 additions & 9 deletions compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,26 +91,26 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
myForwardMapping = myForwardMapping.updated(name, name)
myReverseMapping = myReverseMapping.updated(name, name)
case TypeBoundsTree(_, tpt) =>
myWildcardImport = true // details are handled separately in impliedBounds
myWildcardImport = true // details are handled separately in wildcardBounds
}
recur(sels1)
case nil =>
}
recur(selectors)
}

private[this] var myDelegateBound: Type = null
private[this] var myWildcardBound: Type = null

def impliedBound(implicit ctx: Context): Type = {
if (myDelegateBound == null)
myDelegateBound = selectors.lastOption match {
def wildcardBound(implicit ctx: Context): Type = {
if (myWildcardBound == null)
myWildcardBound = selectors.lastOption match {
case Some(TypeBoundsTree(_, untpd.TypedSplice(tpt))) => tpt.tpe
case Some(TypeBoundsTree(_, tpt)) =>
myDelegateBound = NoType
myWildcardBound = NoType
ctx.typer.typedAheadType(tpt).tpe
case _ => NoType
}
myDelegateBound
myWildcardBound
}

private def implicitFlags(implicit ctx: Context) =
Expand All @@ -128,8 +128,8 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
val renamed = forwardMapping(ref.name)
if (renamed == ref.name) ref :: Nil
else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil
else if (!impliedBound.exists ||
normalizedCompatible(ref, impliedBound, keepConstraint = false)) ref :: Nil
else if (!wildcardBound.exists ||
normalizedCompatible(ref, wildcardBound, keepConstraint = false)) ref :: Nil
else Nil
}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,9 @@ class Typer extends Namer
var excl = EmptyFlags
if (imp.importDelegate) reqd |= Delegate else excl |= Delegate
var denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx)
if (checkBounds && imp.impliedBound.exists)
if (checkBounds && imp.wildcardBound.exists)
denot = denot.filterWithPredicate(mbr =>
NoViewsAllowed.normalizedCompatible(mbr.info, imp.impliedBound, keepConstraint = false))
NoViewsAllowed.normalizedCompatible(mbr.info, imp.wildcardBound, keepConstraint = false))

// Pass refctx so that any errors are reported in the context of the
// reference instead of the
Expand Down Expand Up @@ -234,7 +234,7 @@ class Typer extends Namer
*/
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type =
if (imp.isWildcardImport && !imp.excluded.contains(name.toTermName) && name != nme.CONSTRUCTOR)
selection(imp, name, checkBounds = imp.importDelegate)
selection(imp, name, checkBounds = true)
else NoType

/** Is (some alternative of) the given predenotation `denot`
Expand Down
Loading