Skip to content

Commit 598c6ad

Browse files
committed
Allow multiple context bounds in {...}
1 parent 2f58cbc commit 598c6ad

File tree

10 files changed

+66
-15
lines changed

10 files changed

+66
-15
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

+2
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,8 @@ object desugar {
11441144
case tree: TypeDef => tree.name.toString
11451145
case tree: AppliedTypeTree if followArgs && tree.args.nonEmpty =>
11461146
s"${apply(x, tree.tpt)}_${extractArgs(tree.args)}"
1147+
case ContextBoundTypeTree(tycon, paramName, _) =>
1148+
s"${apply(x, tycon)}_$paramName"
11471149
case InfixOp(left, op, right) =>
11481150
if followArgs then s"${op.name}_${extractArgs(List(left, right))}"
11491151
else op.name.toString

compiler/src/dotty/tools/dotc/ast/untpd.scala

+8
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
118118
case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree
119119
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
120120
case class ExtMethods(paramss: List[ParamClause], methods: List[Tree])(implicit @constructorOnly src: SourceFile) extends Tree
121+
case class ContextBoundTypeTree(tycon: Tree, paramName: TypeName, ownName: TermName)(implicit @constructorOnly src: SourceFile) extends Tree
121122
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
122123

123124
case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
@@ -677,6 +678,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
677678
def ExtMethods(tree: Tree)(paramss: List[ParamClause], methods: List[Tree])(using Context): Tree = tree match
678679
case tree: ExtMethods if (paramss eq tree.paramss) && (methods == tree.methods) => tree
679680
case _ => finalize(tree, untpd.ExtMethods(paramss, methods)(tree.source))
681+
def ContextBoundTypeTree(tree: Tree)(tycon: Tree, paramName: TypeName, ownName: TermName)(using Context): Tree = tree match
682+
case tree: ContextBoundTypeTree if (tycon eq tree.tycon) && paramName == tree.paramName && ownName == tree.ownName => tree
683+
case _ => finalize(tree, untpd.ContextBoundTypeTree(tycon, paramName, ownName)(tree.source))
680684
def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(using Context): Tree = tree match {
681685
case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree
682686
case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source))
@@ -742,6 +746,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
742746
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
743747
case ExtMethods(paramss, methods) =>
744748
cpy.ExtMethods(tree)(transformParamss(paramss), transformSub(methods))
749+
case ContextBoundTypeTree(tycon, paramName, ownName) =>
750+
cpy.ContextBoundTypeTree(tree)(transform(tycon), paramName, ownName)
745751
case ImportSelector(imported, renamed, bound) =>
746752
cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound))
747753
case Number(_, _) | TypedSplice(_) =>
@@ -797,6 +803,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
797803
this(this(this(x, pats), tpt), rhs)
798804
case ExtMethods(paramss, methods) =>
799805
this(paramss.foldLeft(x)(apply), methods)
806+
case ContextBoundTypeTree(tycon, paramName, ownName) =>
807+
this(x, tycon)
800808
case ImportSelector(imported, renamed, bound) =>
801809
this(this(this(x, imported), renamed), bound)
802810
case Number(_, _) =>

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+8-3
Original file line numberDiff line numberDiff line change
@@ -2205,11 +2205,16 @@ object Parsers {
22052205
else atSpan((t.span union cbs.head.span).start) { ContextBounds(t, cbs) }
22062206
}
22072207

2208+
/** ContextBound ::= Type [`as` id] */
2209+
def contextBound(pname: TypeName): Tree =
2210+
ContextBoundTypeTree(toplevelTyp(), pname, EmptyTermName)
2211+
22082212
def contextBounds(pname: TypeName): List[Tree] =
22092213
if in.isColon then
2210-
atSpan(in.skipToken()) {
2211-
AppliedTypeTree(toplevelTyp(), Ident(pname))
2212-
} :: contextBounds(pname)
2214+
in.nextToken()
2215+
if in.token == LBRACE && in.featureEnabled(Feature.modularity)
2216+
then inBraces(commaSeparated(() => contextBound(pname)))
2217+
else contextBound(pname) :: contextBounds(pname)
22132218
else if in.token == VIEWBOUND then
22142219
report.errorOrMigrationWarning(
22152220
em"view bounds `<%' are no longer supported, use a context bound `:' instead",

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

+16-5
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
386386
changePrec(GlobalPrec) { keywordStr("for ") ~ Text(enums map enumText, "; ") ~ sep ~ toText(expr) }
387387

388388
def cxBoundToText(bound: untpd.Tree): Text = bound match { // DD
389-
case AppliedTypeTree(tpt, _) => " : " ~ toText(tpt)
389+
case ContextBoundTypeTree(tpt, _, _) => " : " ~ toText(tpt)
390390
case untpd.Function(_, tpt) => " <% " ~ toText(tpt)
391391
}
392392

@@ -658,7 +658,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
658658
def toTextAnnot =
659659
toTextLocal(arg) ~~ annotText(annot.symbol.enclosingClass, annot)
660660
def toTextRetainsAnnot =
661-
try changePrec(GlobalPrec)(toText(arg) ~ "^" ~ toTextCaptureSet(captureSet))
661+
try changePrec(GlobalPrec)(toTextLocal(arg) ~ "^" ~ toTextCaptureSet(captureSet))
662662
catch case ex: IllegalCaptureRef => toTextAnnot
663663
if annot.symbol.maybeOwner.isRetains
664664
&& Feature.ccEnabled && !printDebug
@@ -747,9 +747,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
747747
case GenAlias(pat, expr) =>
748748
toText(pat) ~ " = " ~ toText(expr)
749749
case ContextBounds(bounds, cxBounds) =>
750-
cxBounds.foldLeft(toText(bounds)) {(t, cxb) =>
751-
t ~ cxBoundToText(cxb)
752-
}
750+
if Feature.enabled(Feature.modularity) then
751+
def boundsText(bounds: Tree) = bounds match
752+
case ContextBoundTypeTree(tpt, _, ownName) =>
753+
toText(tpt) ~ (" as " ~ toText(ownName) `provided` !ownName.isEmpty)
754+
case bounds => toText(bounds)
755+
cxBounds match
756+
case bound :: Nil => ": " ~ boundsText(bound)
757+
case _ => ": {" ~ Text(cxBounds.map(boundsText), ", ") ~ "}"
758+
else
759+
cxBounds.foldLeft(toText(bounds)) {(t, cxb) =>
760+
t ~ cxBoundToText(cxb)
761+
}
753762
case PatDef(mods, pats, tpt, rhs) =>
754763
modText(mods, NoSymbol, keywordStr("val"), isType = false) ~~
755764
toText(pats, ", ") ~ optAscription(tpt) ~ optText(rhs)(" = " ~ _)
@@ -794,6 +803,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
794803
prefix ~~ idx.toString ~~ "|" ~~ tpeText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix
795804
case CapturesAndResult(refs, parent) =>
796805
changePrec(GlobalPrec)("^{" ~ Text(refs.map(toText), ", ") ~ "}" ~ toText(parent))
806+
case ContextBoundTypeTree(tycon, pname, ownName) =>
807+
toText(pname) ~ " : " ~ toText(tycon) ~ (" as " ~ toText(ownName) `provided` !ownName.isEmpty)
797808
case _ =>
798809
tree.fallbackToText(this)
799810
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

+11
Original file line numberDiff line numberDiff line change
@@ -2284,6 +2284,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
22842284
tree.tpFun(tsyms, vsyms)
22852285
completeTypeTree(InferredTypeTree(), tp, tree)
22862286

2287+
def typedContextBoundTypeTree(tree: untpd.ContextBoundTypeTree)(using Context): Tree =
2288+
val tycon = typedType(tree.tycon)
2289+
val tyconSplice = untpd.TypedSplice(tycon)
2290+
val tparam = untpd.Ident(tree.paramName).withSpan(tree.span)
2291+
if tycon.tpe.typeParams.nonEmpty then
2292+
typed(untpd.AppliedTypeTree(tyconSplice, tparam :: Nil))
2293+
else
2294+
errorTree(tree,
2295+
em"""Illegal context bound: ${tycon.tpe} does not take type parameters.""")
2296+
22872297
def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(using Context): SingletonTypeTree = {
22882298
val ref1 = typedExpr(tree.ref, SingletonTypeProto)
22892299
checkStable(ref1.tpe, tree.srcPos, "singleton type")
@@ -3269,6 +3279,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
32693279
case tree: untpd.UnApply => typedUnApply(tree, pt)
32703280
case tree: untpd.Tuple => typedTuple(tree, pt)
32713281
case tree: untpd.InLambdaTypeTree => typedInLambdaTypeTree(tree, pt)
3282+
case tree: untpd.ContextBoundTypeTree => typedContextBoundTypeTree(tree)
32723283
case tree: untpd.InfixOp => typedInfixOp(tree, pt)
32733284
case tree: untpd.ParsedTry => typedTry(tree, pt)
32743285
case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt)

tests/neg/i9330.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
val x = {
2-
() == "" // error
2+
() == ""
33
implicit def foo[A: A] // error // error // error
44
}

tests/pos/FromString-typeparam.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//> using options -language:experimental.modularity -source future
2+
3+
trait FromString[A]:
4+
def fromString(s: String): A
5+
6+
given FromString[Int] = _.toInt
7+
8+
given FromString[Double] = _.toDouble
9+
10+
def add[N: {FromString, Numeric}](a: String, b: String): N =
11+
val num = summon[Numeric[N]]
12+
val N = summon[FromString[N]]
13+
num.plus(N.fromString(a), N.fromString(b))

tests/semanticdb/expect/Methods.expect.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Methods/*<-example::Methods#*/[T/*<-example::Methods#[T]*/] {
1515
def m6/*<-example::Methods#m6().*/(x/*<-example::Methods#m6().(x)*/: Int/*->scala::Int#*/) = ???/*->scala::Predef.`???`().*/
1616
def m6/*<-example::Methods#m6(+1).*/(x/*<-example::Methods#m6(+1).(x)*/: List/*->example::Methods#List#*/[T/*->example::Methods#[T]*/]) = ???/*->scala::Predef.`???`().*/
1717
def m6/*<-example::Methods#m6(+2).*/(x/*<-example::Methods#m6(+2).(x)*/: scala.List/*->scala::package.List#*/[T/*->example::Methods#[T]*/]) = ???/*->scala::Predef.`???`().*/
18-
def m7/*<-example::Methods#m7().*/[U/*<-example::Methods#m7().[U]*//*<-example::Methods#m7().(evidence$1)*/: Ordering/*->scala::math::Ordering#*/](c/*<-example::Methods#m7().(c)*/: Methods/*->example::Methods#*/[T/*->example::Methods#[T]*/], l/*<-example::Methods#m7().(l)*/: List/*->example::Methods#List#*/[U/*->example::Methods#m7().[U]*/]) = ???/*->scala::Predef.`???`().*/
18+
def m7/*<-example::Methods#m7().*/[U/*<-example::Methods#m7().[U]*/: Ordering/*->example::Methods#m7().[U]*//*<-example::Methods#m7().(evidence$1)*/](c/*<-example::Methods#m7().(c)*/: Methods/*->example::Methods#*/[T/*->example::Methods#[T]*/], l/*<-example::Methods#m7().(l)*/: List/*->example::Methods#List#*/[U/*->example::Methods#m7().[U]*/]) = ???/*->scala::Predef.`???`().*/
1919
def `m8()./*<-example::Methods#`m8().`().*/`() = ???/*->scala::Predef.`???`().*/
2020
class `m9()./*<-example::Methods#`m9().`#*/`
2121
def m9/*<-example::Methods#m9().*/(x/*<-example::Methods#m9().(x)*/: `m9().`/*->example::Methods#`m9().`#*/) = ???/*->scala::Predef.`???`().*/

tests/semanticdb/expect/Synthetic.expect.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class Synthetic/*<-example::Synthetic#*/ {
3030
null.asInstanceOf/*->scala::Any#asInstanceOf().*/[Int/*->scala::Int#*/ => Int/*->scala::Int#*/](2)
3131
}
3232

33-
class J/*<-example::Synthetic#J#*/[T/*<-example::Synthetic#J#[T]*//*<-example::Synthetic#J#evidence$1.*/: Manifest/*->scala::Predef.Manifest#*/] { val arr/*<-example::Synthetic#J#arr.*/ = Array/*->scala::Array.*/.empty/*->scala::Array.empty().*/[T/*->example::Synthetic#J#[T]*/] }
33+
class J/*<-example::Synthetic#J#*/[T/*<-example::Synthetic#J#[T]*/: /*<-example::Synthetic#J#evidence$1.*/Manifest/*->scala::Predef.Manifest#*//*->example::Synthetic#J#[T]*/] { val arr/*<-example::Synthetic#J#arr.*/ = Array/*->scala::Array.*/.empty/*->scala::Array.empty().*/[T/*->example::Synthetic#J#[T]*/] }
3434

3535
class F/*<-example::Synthetic#F#*/
3636
implicit val ordering/*<-example::Synthetic#ordering.*/: Ordering/*->scala::package.Ordering#*/[F/*->example::Synthetic#F#*/] = ???/*->scala::Predef.`???`().*/

tests/semanticdb/metac.expect

+5-4
Original file line numberDiff line numberDiff line change
@@ -2732,8 +2732,8 @@ Occurrences:
27322732
[16:29..16:32): ??? -> scala/Predef.`???`().
27332733
[17:6..17:8): m7 <- example/Methods#m7().
27342734
[17:9..17:10): U <- example/Methods#m7().[U]
2735-
[17:10..17:10): <- example/Methods#m7().(evidence$1)
2736-
[17:12..17:20): Ordering -> scala/math/Ordering#
2735+
[17:12..17:20): Ordering -> example/Methods#m7().[U]
2736+
[17:12..17:12): <- example/Methods#m7().(evidence$1)
27372737
[17:22..17:23): c <- example/Methods#m7().(c)
27382738
[17:25..17:32): Methods -> example/Methods#
27392739
[17:33..17:34): T -> example/Methods#[T]
@@ -3533,7 +3533,7 @@ Uri => Synthetic.scala
35333533
Text => empty
35343534
Language => Scala
35353535
Symbols => 52 entries
3536-
Occurrences => 136 entries
3536+
Occurrences => 137 entries
35373537
Synthetics => 39 entries
35383538

35393539
Symbols:
@@ -3659,8 +3659,9 @@ Occurrences:
36593659
[32:8..32:9): J <- example/Synthetic#J#
36603660
[32:9..32:9): <- example/Synthetic#J#`<init>`().
36613661
[32:10..32:11): T <- example/Synthetic#J#[T]
3662-
[32:11..32:11): <- example/Synthetic#J#evidence$1.
3662+
[32:13..32:13): <- example/Synthetic#J#evidence$1.
36633663
[32:13..32:21): Manifest -> scala/Predef.Manifest#
3664+
[32:13..32:21): Manifest -> example/Synthetic#J#[T]
36643665
[32:29..32:32): arr <- example/Synthetic#J#arr.
36653666
[32:35..32:40): Array -> scala/Array.
36663667
[32:41..32:46): empty -> scala/Array.empty().

0 commit comments

Comments
 (0)