From 556230ad15c7a2587d6e34496dae00fc310efe42 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Jan 2016 10:40:31 +0100 Subject: Check named type params for welformedness rules. --- test/dotc/tests.scala | 1 + 1 file changed, 1 insertion(+) (limited to 'test') diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 421846ca2..1f4ff2da4 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -122,6 +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 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) -- cgit v1.2.3 From 8441de7a907996361a78d744d3364cee3d558f84 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 29 Jan 2016 21:48:55 +0100 Subject: Allow Named Arguments in TypeArgs Lets one also pass named arguments to methods. --- docs/SyntaxSummary.txt | 2 +- src/dotty/tools/dotc/ast/tpd.scala | 2 +- src/dotty/tools/dotc/parsing/Parsers.scala | 4 +-- src/dotty/tools/dotc/transform/PostTyper.scala | 42 ++++++++++++++++++++++-- src/dotty/tools/dotc/typer/Applications.scala | 11 +++++-- src/dotty/tools/dotc/typer/TypeAssigner.scala | 44 +++++++++++++++++++++++--- src/dotty/tools/dotc/typer/Typer.scala | 6 +--- test/dotc/tests.scala | 2 +- tests/neg/named-params.scala | 12 +++++++ tests/pos/named-params.scala | 16 +++++++++- 10 files changed, 121 insertions(+), 20 deletions(-) (limited to 'test') 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)})) } + + -- cgit v1.2.3