Skip to content

Commit 8441de7

Browse files
committed
Allow Named Arguments in TypeArgs
Lets one also pass named arguments to methods.
1 parent 1d585f1 commit 8441de7

File tree

10 files changed

+121
-20
lines changed

10 files changed

+121
-20
lines changed

docs/SyntaxSummary.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ grammar.
162162
| `_'
163163
| `(' ExprsInParens `)' Parens(exprs)
164164
| SimpleExpr `.' id Select(expr, id)
165-
| SimpleExpr TypeArgs TypeApply(expr, args)
165+
| SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args)
166166
| SimpleExpr1 ArgumentExprs Apply(expr, args)
167167
| XmlExpr
168168
ExprsInParens ::= ExprInParens {`,' ExprInParens}

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
6767
def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed =
6868
ta.assignType(untpd.Typed(expr, tpt), tpt)
6969

70-
def NamedArg(name: Name, arg: Tree)(implicit ctx: Context) =
70+
def NamedArg(name: Name, arg: Tree)(implicit ctx: Context): NamedArg =
7171
ta.assignType(untpd.NamedArg(name, arg), arg)
7272

7373
def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign =

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,7 +1075,7 @@ object Parsers {
10751075
* | Path
10761076
* | `(' [ExprsInParens] `)'
10771077
* | SimpleExpr `.' Id
1078-
* | SimpleExpr TypeArgs
1078+
* | SimpleExpr (TypeArgs | NamedTypeArgs)
10791079
* | SimpleExpr1 ArgumentExprs
10801080
*/
10811081
def simpleExpr(): Tree = {
@@ -1124,7 +1124,7 @@ object Parsers {
11241124
in.nextToken()
11251125
simpleExprRest(selector(t), canApply = true)
11261126
case LBRACKET =>
1127-
val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs()) }
1127+
val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs(namedOK = true)) }
11281128
simpleExprRest(tapp, canApply = true)
11291129
case LPAREN | LBRACE if canApply =>
11301130
val app = atPos(t.pos.start, in.offset) { Apply(t, argumentExprs()) }

src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,41 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
143143
}
144144
}
145145

146+
private def normalizeTypeArgs(tree: TypeApply)(implicit ctx: Context): TypeApply = tree.tpe match {
147+
case pt: PolyType => // wait for more arguments coming
148+
tree
149+
case _ =>
150+
def decompose(tree: TypeApply): (Tree, List[Tree]) = tree.fun match {
151+
case fun: TypeApply =>
152+
val (tycon, args) = decompose(fun)
153+
(tycon, args ++ tree.args)
154+
case _ =>
155+
(tree.fun, tree.args)
156+
}
157+
def reorderArgs(pnames: List[Name], namedArgs: List[NamedArg], otherArgs: List[Tree]): List[Tree] = pnames match {
158+
case pname :: pnames1 =>
159+
namedArgs.partition(_.name == pname) match {
160+
case (NamedArg(_, arg) :: _, namedArgs1) =>
161+
arg :: reorderArgs(pnames1, namedArgs1, otherArgs)
162+
case _ =>
163+
val otherArg :: otherArgs1 = otherArgs
164+
otherArg :: reorderArgs(pnames1, namedArgs, otherArgs1)
165+
}
166+
case nil =>
167+
assert(namedArgs.isEmpty && otherArgs.isEmpty)
168+
Nil
169+
}
170+
val (tycon, args) = decompose(tree)
171+
tycon.tpe.widen match {
172+
case PolyType(pnames) =>
173+
val (namedArgs, otherArgs) = args.partition(isNamedArg)
174+
val args1 = reorderArgs(pnames, namedArgs.asInstanceOf[List[NamedArg]], otherArgs)
175+
TypeApply(tycon, args1).withPos(tree.pos).withType(tree.tpe)
176+
case _ =>
177+
tree
178+
}
179+
}
180+
146181
override def transform(tree: Tree)(implicit ctx: Context): Tree =
147182
try normalizeTree(tree) match {
148183
case tree: Ident =>
@@ -152,15 +187,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
152187
}
153188
case tree: Select =>
154189
transformSelect(paramFwd.adaptRef(tree), Nil)
155-
case tree @ TypeApply(fn, args) =>
190+
case tree: TypeApply =>
191+
val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
156192
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
157193
fn match {
158194
case sel: Select =>
159195
val args1 = transform(args)
160196
val sel1 = transformSelect(sel, args1)
161-
if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1)
197+
if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree1)(sel1, args1)
162198
case _ =>
163-
super.transform(tree)
199+
super.transform(tree1)
164200
}
165201
case tree @ Assign(sel: Select, _) =>
166202
superAcc.transformAssign(super.transform(tree))

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -603,12 +603,19 @@ trait Applications extends Compatibility { self: Typer =>
603603
protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree =
604604
throw new Error(s"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")
605605

606+
def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context) =
607+
for (arg @ NamedArg(id, argtpt) <- args) yield {
608+
val argtpt1 = typedType(argtpt)
609+
cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe)
610+
}
611+
606612
def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") {
607-
var typedArgs = tree.args mapconserve (typedType(_))
613+
val isNamed = hasNamedArg(tree.args)
614+
var typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
608615
val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt))
609616
typedFn.tpe.widen match {
610617
case pt: PolyType =>
611-
if (typedArgs.length <= pt.paramBounds.length)
618+
if (typedArgs.length <= pt.paramBounds.length && !isNamed)
612619
typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg)
613620
case _ =>
614621
}

src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._,
88
import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._
99
import util.Positions._
1010
import config.Printers._
11-
import NameOps._
11+
import ast.Trees._
12+
import collection.mutable
1213

1314
trait TypeAssigner {
1415
import tpd._
@@ -310,9 +311,44 @@ trait TypeAssigner {
310311
def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = {
311312
val ownType = fn.tpe.widen match {
312313
case pt: PolyType =>
313-
val argTypes = args.tpes
314-
if (sameLength(argTypes, pt.paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
315-
else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos)
314+
val paramNames = pt.paramNames
315+
if (hasNamedArg(args)) {
316+
val argMap = new mutable.HashMap[Name, Type]
317+
for (NamedArg(name, arg) <- args)
318+
if (argMap.contains(name))
319+
ctx.error("duplicate name", arg.pos)
320+
else if (!paramNames.contains(name))
321+
ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos)
322+
else
323+
argMap(name) = arg.tpe
324+
val gapBuf = new mutable.ListBuffer[Int]
325+
def nextPoly = {
326+
val idx = gapBuf.length
327+
gapBuf += idx
328+
PolyParam(pt, idx)
329+
}
330+
val normArgs = paramNames.map(pname => argMap.getOrElse(pname, nextPoly))
331+
val transform = new TypeMap {
332+
def apply(t: Type) = t match {
333+
case PolyParam(`pt`, idx) => normArgs(idx)
334+
case _ => mapOver(t)
335+
}
336+
}
337+
val resultType1 = transform(pt.resultType)
338+
if (gapBuf.isEmpty) resultType1
339+
else {
340+
val gaps = gapBuf.toList
341+
pt.derivedPolyType(
342+
gaps.map(paramNames.filterNot(argMap.contains)),
343+
gaps.map(idx => transform(pt.paramBounds(idx)).bounds),
344+
resultType1)
345+
}
346+
}
347+
else {
348+
val argTypes = args.tpes
349+
if (sameLength(argTypes, paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
350+
else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos)
351+
}
316352
case _ =>
317353
errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos)
318354
}

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -893,11 +893,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
893893
else {
894894
var args = tree.args
895895
val args1 =
896-
if (hasNamedArg(args))
897-
for (arg @ NamedArg(id, argtpt) <- args) yield {
898-
val argtpt1 = typedType(argtpt)
899-
cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe)
900-
}
896+
if (hasNamedArg(args)) typedNamedArgs(args)
901897
else {
902898
if (args.length != tparams.length) {
903899
ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos)

test/dotc/tests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class tests extends CompilerTest {
122122
@Test def neg_autoTupling = compileFile(posDir, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil, xerrors = 3)
123123
@Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 3)
124124
@Test def neg_companions = compileFile(negDir, "companions", xerrors = 1)
125-
@Test def namedParams = compileFile(negDir, "named-params", xerrors = 3)
125+
@Test def namedParams = compileFile(negDir, "named-params", xerrors = 14)
126126
@Test def neg_over = compileFile(negDir, "over", xerrors = 3)
127127
@Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 14)
128128
@Test def neg_overrideClass = compileFile(negDir, "overrideClass", List("-language:Scala2"), xerrors = 1)

tests/neg/named-params.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,16 @@ object Test {
1919
val c3 = new C[Elem = String, Value = Int]("B")
2020
val c4 = new C[Elem = String]("C")
2121
val x2: c2.Elem = c2.elem
22+
23+
val c5 = new C[Elem1 = String, Value0 = Int]("B") // error // error
24+
25+
def d2[E, V](x: E) = new C[Elem = E, Value = V](x)
26+
27+
val dup = d2[E = Int, V = String, E = Boolean](2) // error
28+
val z1 = d2[Elem = Int, Value = String](1) // error // error
29+
val z2 = d2[Value = String, Elem = Int](1) // error // error
30+
val z3 = d2[Elem = Int](1) // error
31+
val z4 = d2[Value = Int]("AAA") // error
32+
val z5 = d2[Elem = Int][Value = String](1) //error // error
33+
2234
}

tests/pos/named-params.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class C[type Elem, type Value](val elem: Elem) {
44
def toVal: Elem = ???
55
}
66

7-
abstract class D[type Elem, V](elem: Elem) extends C[Elem, V](elem)
7+
class D[type Elem, V](elem: Elem) extends C[Elem, V](elem)
88

99
object Test {
1010
val c = new C[String, String]("A") {
@@ -17,6 +17,18 @@ object Test {
1717
val c3 = new C[Elem = String, Value = Int]("B")
1818
val c4 = new C[Elem = String]("C")
1919
val x2: c2.Elem = c2.elem
20+
21+
def d1[E, V](x: E) = new D[E, V](x)
22+
def d2[E, V](x: E) = new C[Elem = E, Value = V](x)
23+
24+
val y1 = d1[Int, String](1)
25+
val y2 = d1[E = Int](2)
26+
val y3 = d1[V = String](3)
27+
val z1 = d2[E = Int, V = String](1)
28+
val z2 = d2[V = String, E = Int](1)
29+
val z3 = d2[E = Int](1)
30+
val z4 = d2[V = Int]("AAA")
31+
val z5 = d2[E = Int][V = String](1)
2032
}
2133

2234
// Adapated from i94-nada
@@ -30,3 +42,5 @@ trait Test1 {
3042
def flatMap[X,Y,M <: Monad](m: M[Elem = X], f: X => M[Elem = Y]): M[Elem = Y] = f(m.unit)
3143
println(flatMap(Left(1), {x: Int => Left(x)}))
3244
}
45+
46+

0 commit comments

Comments
 (0)