aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/SyntaxSummary.txt2
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala2
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala4
-rw-r--r--src/dotty/tools/dotc/transform/PostTyper.scala42
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala11
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala44
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala6
-rw-r--r--test/dotc/tests.scala2
-rw-r--r--tests/neg/named-params.scala12
-rw-r--r--tests/pos/named-params.scala16
10 files changed, 121 insertions, 20 deletions
diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt
index 78f59e6d1..d4f7ceade 100644
--- a/docs/SyntaxSummary.txt
+++ b/docs/SyntaxSummary.txt
@@ -162,7 +162,7 @@ grammar.
| `_'
| `(' ExprsInParens `)' Parens(exprs)
| SimpleExpr `.' id Select(expr, id)
- | SimpleExpr TypeArgs TypeApply(expr, args)
+ | SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args)
| SimpleExpr1 ArgumentExprs Apply(expr, args)
| XmlExpr
ExprsInParens ::= ExprInParens {`,' ExprInParens}
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index b78e4c79f..8e52d695b 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -67,7 +67,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed =
ta.assignType(untpd.Typed(expr, tpt), tpt)
- def NamedArg(name: Name, arg: Tree)(implicit ctx: Context) =
+ def NamedArg(name: Name, arg: Tree)(implicit ctx: Context): NamedArg =
ta.assignType(untpd.NamedArg(name, arg), arg)
def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign =
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index 58ec75605..bb8fbe08b 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -1075,7 +1075,7 @@ object Parsers {
* | Path
* | `(' [ExprsInParens] `)'
* | SimpleExpr `.' Id
- * | SimpleExpr TypeArgs
+ * | SimpleExpr (TypeArgs | NamedTypeArgs)
* | SimpleExpr1 ArgumentExprs
*/
def simpleExpr(): Tree = {
@@ -1124,7 +1124,7 @@ object Parsers {
in.nextToken()
simpleExprRest(selector(t), canApply = true)
case LBRACKET =>
- val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs()) }
+ val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs(namedOK = true)) }
simpleExprRest(tapp, canApply = true)
case LPAREN | LBRACE if canApply =>
val app = atPos(t.pos.start, in.offset) { Apply(t, argumentExprs()) }
diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala
index b38858b72..d552c16f7 100644
--- a/src/dotty/tools/dotc/transform/PostTyper.scala
+++ b/src/dotty/tools/dotc/transform/PostTyper.scala
@@ -143,6 +143,41 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
}
}
+ private def normalizeTypeArgs(tree: TypeApply)(implicit ctx: Context): TypeApply = tree.tpe match {
+ case pt: PolyType => // wait for more arguments coming
+ tree
+ case _ =>
+ def decompose(tree: TypeApply): (Tree, List[Tree]) = tree.fun match {
+ case fun: TypeApply =>
+ val (tycon, args) = decompose(fun)
+ (tycon, args ++ tree.args)
+ case _ =>
+ (tree.fun, tree.args)
+ }
+ def reorderArgs(pnames: List[Name], namedArgs: List[NamedArg], otherArgs: List[Tree]): List[Tree] = pnames match {
+ case pname :: pnames1 =>
+ namedArgs.partition(_.name == pname) match {
+ case (NamedArg(_, arg) :: _, namedArgs1) =>
+ arg :: reorderArgs(pnames1, namedArgs1, otherArgs)
+ case _ =>
+ val otherArg :: otherArgs1 = otherArgs
+ otherArg :: reorderArgs(pnames1, namedArgs, otherArgs1)
+ }
+ case nil =>
+ assert(namedArgs.isEmpty && otherArgs.isEmpty)
+ Nil
+ }
+ val (tycon, args) = decompose(tree)
+ tycon.tpe.widen match {
+ case PolyType(pnames) =>
+ val (namedArgs, otherArgs) = args.partition(isNamedArg)
+ val args1 = reorderArgs(pnames, namedArgs.asInstanceOf[List[NamedArg]], otherArgs)
+ TypeApply(tycon, args1).withPos(tree.pos).withType(tree.tpe)
+ case _ =>
+ tree
+ }
+ }
+
override def transform(tree: Tree)(implicit ctx: Context): Tree =
try normalizeTree(tree) match {
case tree: Ident =>
@@ -152,15 +187,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
}
case tree: Select =>
transformSelect(paramFwd.adaptRef(tree), Nil)
- case tree @ TypeApply(fn, args) =>
+ case tree: TypeApply =>
+ val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
fn match {
case sel: Select =>
val args1 = transform(args)
val sel1 = transformSelect(sel, args1)
- if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1)
+ if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree1)(sel1, args1)
case _ =>
- super.transform(tree)
+ super.transform(tree1)
}
case tree @ Assign(sel: Select, _) =>
superAcc.transformAssign(super.transform(tree))
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index c7acd3c1f..098385d4b 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -603,12 +603,19 @@ trait Applications extends Compatibility { self: Typer =>
protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree =
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}")
+ def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context) =
+ for (arg @ NamedArg(id, argtpt) <- args) yield {
+ val argtpt1 = typedType(argtpt)
+ cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe)
+ }
+
def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") {
- var typedArgs = tree.args mapconserve (typedType(_))
+ val isNamed = hasNamedArg(tree.args)
+ var typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt))
typedFn.tpe.widen match {
case pt: PolyType =>
- if (typedArgs.length <= pt.paramBounds.length)
+ if (typedArgs.length <= pt.paramBounds.length && !isNamed)
typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg)
case _ =>
}
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index 9ee67684d..1c66414a1 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -8,7 +8,8 @@ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._,
import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._
import util.Positions._
import config.Printers._
-import NameOps._
+import ast.Trees._
+import collection.mutable
trait TypeAssigner {
import tpd._
@@ -310,9 +311,44 @@ trait TypeAssigner {
def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = {
val ownType = fn.tpe.widen match {
case pt: PolyType =>
- val argTypes = args.tpes
- if (sameLength(argTypes, pt.paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
- else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos)
+ val paramNames = pt.paramNames
+ if (hasNamedArg(args)) {
+ val argMap = new mutable.HashMap[Name, Type]
+ for (NamedArg(name, arg) <- args)
+ if (argMap.contains(name))
+ ctx.error("duplicate name", arg.pos)
+ else if (!paramNames.contains(name))
+ ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos)
+ else
+ argMap(name) = arg.tpe
+ val gapBuf = new mutable.ListBuffer[Int]
+ def nextPoly = {
+ val idx = gapBuf.length
+ gapBuf += idx
+ PolyParam(pt, idx)
+ }
+ val normArgs = paramNames.map(pname => argMap.getOrElse(pname, nextPoly))
+ val transform = new TypeMap {
+ def apply(t: Type) = t match {
+ case PolyParam(`pt`, idx) => normArgs(idx)
+ case _ => mapOver(t)
+ }
+ }
+ val resultType1 = transform(pt.resultType)
+ if (gapBuf.isEmpty) resultType1
+ else {
+ val gaps = gapBuf.toList
+ pt.derivedPolyType(
+ gaps.map(paramNames.filterNot(argMap.contains)),
+ gaps.map(idx => transform(pt.paramBounds(idx)).bounds),
+ resultType1)
+ }
+ }
+ else {
+ val argTypes = args.tpes
+ if (sameLength(argTypes, paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
+ else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos)
+ }
case _ =>
errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos)
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 0ff2241fb..fc2bf2381 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -893,11 +893,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
else {
var args = tree.args
val args1 =
- if (hasNamedArg(args))
- for (arg @ NamedArg(id, argtpt) <- args) yield {
- val argtpt1 = typedType(argtpt)
- cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe)
- }
+ if (hasNamedArg(args)) typedNamedArgs(args)
else {
if (args.length != tparams.length) {
ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos)
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 1f4ff2da4..6a61ec62a 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -122,7 +122,7 @@ class tests extends CompilerTest {
@Test def neg_autoTupling = compileFile(posDir, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil, xerrors = 3)
@Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 3)
@Test def neg_companions = compileFile(negDir, "companions", xerrors = 1)
- @Test def namedParams = compileFile(negDir, "named-params", xerrors = 3)
+ @Test def namedParams = compileFile(negDir, "named-params", xerrors = 14)
@Test def neg_over = compileFile(negDir, "over", xerrors = 3)
@Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 14)
@Test def neg_overrideClass = compileFile(negDir, "overrideClass", List("-language:Scala2"), xerrors = 1)
diff --git a/tests/neg/named-params.scala b/tests/neg/named-params.scala
index c1f055e10..9ef4ed066 100644
--- a/tests/neg/named-params.scala
+++ b/tests/neg/named-params.scala
@@ -19,4 +19,16 @@ object Test {
val c3 = new C[Elem = String, Value = Int]("B")
val c4 = new C[Elem = String]("C")
val x2: c2.Elem = c2.elem
+
+ val c5 = new C[Elem1 = String, Value0 = Int]("B") // error // error
+
+ def d2[E, V](x: E) = new C[Elem = E, Value = V](x)
+
+ val dup = d2[E = Int, V = String, E = Boolean](2) // error
+ val z1 = d2[Elem = Int, Value = String](1) // error // error
+ val z2 = d2[Value = String, Elem = Int](1) // error // error
+ val z3 = d2[Elem = Int](1) // error
+ val z4 = d2[Value = Int]("AAA") // error
+ val z5 = d2[Elem = Int][Value = String](1) //error // error
+
}
diff --git a/tests/pos/named-params.scala b/tests/pos/named-params.scala
index 9668ec34c..bcd64ea4b 100644
--- a/tests/pos/named-params.scala
+++ b/tests/pos/named-params.scala
@@ -4,7 +4,7 @@ class C[type Elem, type Value](val elem: Elem) {
def toVal: Elem = ???
}
-abstract class D[type Elem, V](elem: Elem) extends C[Elem, V](elem)
+class D[type Elem, V](elem: Elem) extends C[Elem, V](elem)
object Test {
val c = new C[String, String]("A") {
@@ -17,6 +17,18 @@ object Test {
val c3 = new C[Elem = String, Value = Int]("B")
val c4 = new C[Elem = String]("C")
val x2: c2.Elem = c2.elem
+
+ def d1[E, V](x: E) = new D[E, V](x)
+ def d2[E, V](x: E) = new C[Elem = E, Value = V](x)
+
+ val y1 = d1[Int, String](1)
+ val y2 = d1[E = Int](2)
+ val y3 = d1[V = String](3)
+ val z1 = d2[E = Int, V = String](1)
+ val z2 = d2[V = String, E = Int](1)
+ val z3 = d2[E = Int](1)
+ val z4 = d2[V = Int]("AAA")
+ val z5 = d2[E = Int][V = String](1)
}
// Adapated from i94-nada
@@ -30,3 +42,5 @@ trait Test1 {
def flatMap[X,Y,M <: Monad](m: M[Elem = X], f: X => M[Elem = Y]): M[Elem = Y] = f(m.unit)
println(flatMap(Left(1), {x: Int => Left(x)}))
}
+
+