Skip to content

Allow given in constructor applications #6488

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 8 commits into from
May 16, 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
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/NavigateAST.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,21 @@ object NavigateAST {
*/
def pathTo(span: Span, from: Positioned, skipZeroExtent: Boolean = false)(implicit ctx: Context): List[Positioned] = {
def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = {
var bestFit: List[Positioned] = path
while (it.hasNext) {
val path1 = it.next() match {
case p: Positioned => singlePath(p, path)
case m: untpd.Modifiers => childPath(m.productIterator, path)
case xs: List[_] => childPath(xs.iterator, path)
case _ => path
}
if (path1 ne path) return path1
if ((path1 ne path) &&
((bestFit eq path) ||
bestFit.head.span != path1.head.span &&
bestFit.head.span.contains(path1.head.span)))
bestFit = path1
}
path
bestFit
}
def singlePath(p: Positioned, path: List[Positioned]): List[Positioned] =
if (p.span.exists && !(skipZeroExtent && p.span.isZeroExtent) && p.span.contains(span)) {
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Positioned.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro
}
}

/** A hook that can be overridden if overlap checking in `checkPos` should be
* disabled for this node.
*/
def disableOverlapChecks = false

/** Check that all positioned items in this tree satisfy the following conditions:
* - Parent spans contain child spans
* - If item is a non-empty tree, it has a position
Expand All @@ -169,7 +174,7 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro
s"position error: position not set for $tree # ${tree.uniqueId}")
case _ =>
}
if (nonOverlapping) {
if (nonOverlapping && !disableOverlapChecks) {
this match {
case _: XMLBlock =>
// FIXME: Trees generated by the XML parser do not satisfy `checkPos`
Expand Down
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,10 @@ object Trees {
assert(tpt != genericEmptyTree)
def unforced: LazyTree = preRhs
protected def force(x: AnyRef): Unit = preRhs = x

override def disableOverlapChecks = rawMods.is(Flags.Implied)
// disable order checks for implicit aliases since their given clause follows
// their for clause, but the two appear swapped in the DefDef.
}

class BackquotedDefDef[-T >: Untyped] private[ast] (name: TermName, tparams: List[TypeDef[T]],
Expand Down Expand Up @@ -783,6 +787,10 @@ object Trees {

def parents: List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate
def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate

override def disableOverlapChecks = true
// disable overlaps checks since templates of instance definitions have their
// `given` clause come last, which means that the constructor span can contain the parent spans.
}


Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
vdef.withMods(mods | Param)
}

def makeSyntheticParameter(n: Int = 1, tpt: Tree = null, flags: FlagSet = EmptyFlags)(implicit ctx: Context): ValDef =
def makeSyntheticParameter(n: Int = 1, tpt: Tree = null, flags: FlagSet = SyntheticTermParam)(implicit ctx: Context): ValDef =
ValDef(nme.syntheticParamName(n), if (tpt == null) TypeTree() else tpt, EmptyTree)
.withFlags(flags | SyntheticTermParam)
.withFlags(flags)

def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context): Tree =
if (tparams.isEmpty) tpt else LambdaTypeTree(tparams, tpt)
Expand Down
71 changes: 53 additions & 18 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -575,22 +575,26 @@ object Parsers {
}
else recur(operand())
}
else if (in.token == GIVEN) {
else if (in.token == GIVEN && !isType) {
val top1 = reduceStack(base, top, minInfixPrec, leftAssoc = true, nme.WITHkw, isType)
assert(opStack `eq` base)
val app = atSpan(startOffset(top1), in.offset) {
in.nextToken()
val args = if (in.token == LPAREN) parArgumentExprs() else operand() :: Nil
Apply(top, args)
}
app.pushAttachment(ApplyGiven, ())
recur(app)
recur(applyGiven(top1, operand))
}
else reduceStack(base, top, minPrec, leftAssoc = true, in.name, isType)

recur(first)
}

def applyGiven(t: Tree, operand: () => Tree): Tree = {
val app = atSpan(startOffset(t), in.offset) {
in.nextToken()
val args = if (in.token == LPAREN) parArgumentExprs() else operand() :: Nil
Apply(t, args)
}
app.pushAttachment(ApplyGiven, ())
app
}

/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */

/** Accept identifier and return its name as a term name. */
Expand Down Expand Up @@ -2247,7 +2251,8 @@ object Parsers {
val tps = commaSeparated(() => annotType())
var counter = nparams
def nextIdx = { counter += 1; counter }
val params = tps.map(makeSyntheticParameter(nextIdx, _, Given | Implicit))
val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param
val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given | Implicit))
params :: recur(firstClause = false, nparams + params.length)
}
else Nil
Expand Down Expand Up @@ -2673,22 +2678,22 @@ object Parsers {
Template(constr, parents, Nil, EmptyValDef, Nil)
}

/** InstanceDef ::= [id] InstanceParams InstanceBody
/** InstanceDef ::= [id] [DefTypeParamClause] InstanceBody
* InstanceParams ::= [DefTypeParamClause] {GivenParamClause}
* InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody]
* | ‘for’ Type ‘=’ Expr
* InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
* | ‘for’ Type {GivenParamClause} ‘=’ Expr
*/
def instanceDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
var mods1 = addMod(mods, instanceMod)
val name = if (isIdent) ident() else EmptyTermName
val tparams = typeParamClauseOpt(ParamOwner.Def)
val vparamss = paramClauses(ofInstance = true)
val parents =
if (in.token == FOR) {
in.nextToken()
tokenSeparated(COMMA, constrApp)
}
else Nil
val vparamss = paramClauses(ofInstance = true)
val instDef =
if (in.token == EQUALS && parents.length == 1 && parents.head.isType) {
in.nextToken()
Expand All @@ -2709,13 +2714,43 @@ object Parsers {

/* -------- TEMPLATES ------------------------------------------- */

/** ConstrApp ::= SimpleType {ParArgumentExprs}
/** SimpleConstrApp ::= AnnotType {ParArgumentExprs}
* ConstrApp ::= SimpleConstrApp
* | ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
*/
val constrApp: () => Tree = () => {
// Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code
val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR))
if (in.token == LPAREN) parArgumentExprss(wrapNew(t))
else t

def isAnnotType(t: Tree) = t match {
case _: Ident
| _: Select
| _: AppliedTypeTree
| _: Tuple
| _: Parens
| _: RefinedTypeTree
| _: SingletonTypeTree
| _: TypSplice
| _: Annotated => true
case _ => false
}

def givenArgs(t: Tree): Tree = {
if (in.token == GIVEN) givenArgs(applyGiven(t, prefixExpr)) else t
}

if (in.token == LPAREN)
inParens {
val t = toplevelTyp()
if (isAnnotType(t))
if (in.token == LPAREN) givenArgs(parArgumentExprss(wrapNew(t)))
else if (in.token == GIVEN) givenArgs(wrapNew(t))
else t
else Parens(t)
}
else {
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) parArgumentExprss(wrapNew(t)) else t
}
}

/** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1)
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
else if (!ctx.settings.YprintDebug.value && fun.hasType && fun.symbol == defn.InternalQuoted_exprSplice)
keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
else if (tree.getAttachment(untpd.ApplyGiven).isDefined && !homogenizedView)
changePrec(InfixPrec) {
toTextLocal(fun) ~ " given " ~
(if (args.length == 1) toTextLocal(args.head) else "(" ~ toTextGlobal(args, ", ") ~ ")")
}
else
toTextLocal(fun) ~ "(" ~ toTextGlobal(args, ", ") ~ ")"
case tree: TypeApply =>
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2591,6 +2591,8 @@ class Typer extends Namer
tree
else if (wtp.isContextual)
adaptNoArgs(wtp) // insert arguments implicitly
else if (tree.symbol.isPrimaryConstructor && tree.symbol.info.firstParamTypes.isEmpty)
readapt(tree.appliedToNone) // insert () to primary constructors
else
errorTree(tree, em"Missing arguments for $methodStr")
case _ => tryInsertApplyOrImplicit(tree, pt, locked) {
Expand Down
11 changes: 6 additions & 5 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,16 +378,17 @@ 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)
InstanceDef ::= [id] InstanceParams InstanceBody
InstanceDef ::= [id] [DefTypeParamClause] InstanceBody
InstanceParams ::= [DefTypeParamClause] {GivenParamClause}
InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody]
| ‘for’ Type ‘=’ Expr
InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
| ‘for’ Type {GivenParamClause} ‘=’ Expr
Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats)
InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
| ConstrApp {‘,’ ConstrApp}
ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
| ‘(’ ConstrApp {‘given’ (InfixExpr | ParArgumentExprs)} ‘)’
ConstrApp ::= SimpleConstrApp
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
SimpleConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
ConstrExpr ::= SelfInvocation
| ConstrBlock
SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs}
Expand Down
11 changes: 6 additions & 5 deletions docs/docs/reference/contextual-implicit/instance-defs.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@ An implicit instance without type parameters or given clause is created on-deman
Here is the new syntax of implicit instances, seen as a delta from the [standard context free syntax of Scala 3](http://dotty.epfl.ch/docs/internals/syntax.html).
```
TmplDef ::= ...
| ‘implicit’ InstanceDef
| ‘implicit’ InstanceDef
InstanceDef ::= [id] [DefTypeParamClause] InstanceBody
InstanceBody ::= [‘of’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
| ‘of’ Type {GivenParamClause} ‘=’ Expr
ConstrApp ::= AnnotType {ArgumentExprs}
| ‘(’ ConstrApp {‘given’ (InfixExpr | ParArgumentExprs)} ‘)’
InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
| ‘for’ Type {GivenParamClause} ‘=’ Expr
ConstrApp ::= SimpleConstrApp
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
SimpleConstrApp ::= AnnotType {ArgumentExprs}
GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes)
GivenTypes ::= AnnotType {‘,’ AnnotType}
```
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/reference/contextual/instance-defs.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ GivenParamClause ::= ‘given’ (‘(’ [DefParams] ‘)’ | GivenTypes)
InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] [TemplateBody]
| ‘for’ Type ‘=’ Expr
GivenTypes ::= AnnotType {‘,’ AnnotType}
ConstrApp ::= SimpleConstrApp
| ‘(’ SimpleConstrApp {‘given’ (PrefixExpr | ParArgumentExprs)} ‘)’
```
The identifier `id` can be omitted only if either the `for` part or the template body is present.
If the `for` part is missing, the template body must define at least one extension method.
4 changes: 2 additions & 2 deletions tests/neg/i5978.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ trait TokenParser[Token, R]
object TextParser {
implied TP for TokenParser[Char, Position[CharSequence]] {}

implied FromCharToken
given (T: TokenParser[Char, Position[CharSequence]]) for Conversion[Char, Position[CharSequence]] = ???
implied FromCharToken for Conversion[Char, Position[CharSequence]]
given (T: TokenParser[Char, Position[CharSequence]]) = ???
}


Expand Down
2 changes: 1 addition & 1 deletion tests/neg/parser-stability-25.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ class D extends (Int => 1) {
}

class Wrap(x: Int)
class E extends (Wrap)( // error
class E extends (Wrap)( // error // error
// error
29 changes: 29 additions & 0 deletions tests/pos/given-constrapps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class TC
val tc = TC()
class C given (x: TC) {
assert(x eq tc)
}
class C2(n: Int) given (x: TC) given List[TC] {
assert(x eq tc)
the[List[TC]].foreach(t => assert(t eq tc))

def this() given TC given List[TC] = this(1)
}

class D extends (C given tc)
class D2 extends (C2(1) given tc given Nil)

class Foo given TC {
assert(the[TC] != null)
}

object Test extends App {
new (C given tc)
new (C() given tc)
new (C given tc) {}
new (C2(1) given tc given List(tc))
new (C2(1) given tc given List(tc)) {}
new (C2() given tc given List(tc))
def foo given TC = ()
foo given tc
}
4 changes: 2 additions & 2 deletions tests/pos/i5978.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ package p1 {
object TextParser {
implied TP for TokenParser[Char, Position[CharSequence]] {}

implied FromCharToken
given (T: TokenParser[Char, Position[CharSequence]]) for Conversion[Char, Position[CharSequence]] = ???
implied FromCharToken for Conversion[Char, Position[CharSequence]]
given (T: TokenParser[Char, Position[CharSequence]])= ???
}


Expand Down
2 changes: 1 addition & 1 deletion tests/pos/multiversal.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
object Test {
import scala.Eql

implied [X, Y] given Eql[X, Y] for Eql[List[X], List[Y]] = Eql.derived
implied [X, Y] for Eql[List[X], List[Y]] given Eql[X, Y] = Eql.derived

val b: Byte = 1
val c: Char = 2
Expand Down
6 changes: 3 additions & 3 deletions tests/pos/reference/instances.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object Instances extends Common {
if (x < y) -1 else if (x > y) +1 else 0
}

implied ListOrd[T] given Ord[T] for Ord[List[T]] {
implied ListOrd[T] for Ord[List[T]] given Ord[T] {
def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match {
case (Nil, Nil) => 0
case (Nil, _) => -1
Expand Down Expand Up @@ -132,7 +132,7 @@ object Instances extends Common {
println(the[D[Int]])
}
locally {
implied given Context for D[Int]
implied for D[Int] given Context
println(the[D[Int]])
}
}
Expand Down Expand Up @@ -195,7 +195,7 @@ object AnonymousInstances extends Common {
def (xs: List[T]) second[T] = xs.tail.head
}

implied [From, To] given (c: Convertible[From, To]) for Convertible[List[From], List[To]] {
implied [From, To] for Convertible[List[From], List[To]] given (c: Convertible[From, To]) {
def (x: List[From]) convert: List[To] = x.map(c.convert)
}

Expand Down
8 changes: 4 additions & 4 deletions tests/run/implicit-specifity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object Show {
class Generic
object Generic {
implied gen for Generic = new Generic
implied showGen[T] given Generic for Show[T] = new Show[T](2)
implied showGen[T] for Show[T] given Generic = new Show[T](2)
}

class Generic2
Expand All @@ -25,9 +25,9 @@ object SubGen {
object Contextual {
trait Context
implied ctx for Context
implied showGen[T] given Generic for Show[T] = new Show[T](2)
implied showGen[T] given Generic, Context for Show[T] = new Show[T](3)
implied showGen[T] given SubGen for Show[T] = new Show[T](4)
implied showGen[T] for Show[T] given Generic = new Show[T](2)
implied showGen[T] for Show[T] given Generic, Context = new Show[T](3)
implied showGen[T] for Show[T] given SubGen = new Show[T](4)
}

object Test extends App {
Expand Down
2 changes: 1 addition & 1 deletion tests/run/implied-divergence.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ implied e for E(null)

object Test extends App {

implied f given (e: E) for E(e)
implied f for E(e) given (e: E)

assert(the[E].toString == "E(E(null))")

Expand Down
Loading