From e8aecfa4d48383321549aa8f1ec8d1edb0ccaf06 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 Jul 2015 18:42:17 +0200 Subject: Disallow wildcard arguments to higher-kinded types... ...unless the HK type can be eta-reduced to a class type. --- src/dotty/tools/dotc/typer/Typer.scala | 29 +++++++++++++++++++++++++++- test/dotc/tests.scala | 1 + tests/neg/partialApplications.scala | 11 +++++++++++ tests/pos/partialApplications.scala | 35 +++++++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/neg/partialApplications.scala diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index d35356a85..a3c64f526 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -844,7 +844,34 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val args1 = args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] // check that arguments conform to bounds is done in phase PostTyper - assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) + val tree1 = assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) + if (tree1.tpe.isHKApply) + for (arg @ TypeBoundsTree(_, _) <- args1) + ctx.error("illegal wildcard type argument; does not correspond to type parameter of a class", arg.pos) + // The reason for outlawing such arguments is illustrated by the following example. + // Say we have + // + // type RMap[A, B] = Map[B, A] + // + // Then + // + // Rmap[_, Int] + // + // translates to + // + // Lambda$I { type hk$0; type hk$1 = Int; type $apply = Map[$hk1, $hk0] } # $apply + // + // Let's call the last type T. You would expect that + // + // Map[Int, String] <: RMap[_, Int] + // + // But that's not the case given the standard subtyping rules. In fact, the rhs reduces to + // + // Map[Int, T # $hk0] + // + // That means the second argument to `Map` is unknown and String is certainly not a subtype of it. + // To avoid the surprise we outlaw problematic wildcard arguments from the start. + tree1 } } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 86d82be76..b8423db38 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -155,6 +155,7 @@ class tests extends CompilerTest { @Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2) @Test def neg_firstError = compileFile(negDir, "firstError", xerrors = 3) @Test def neg_implicitLowerBound = compileFile(negDir, "implicit-lower-bound", xerrors = 1) + @Test def neg_partialApplications = compileFile(negDir, "partialApplications", xerrors = 8) @Test def run_all = runFiles(runDir) diff --git a/tests/neg/partialApplications.scala b/tests/neg/partialApplications.scala new file mode 100644 index 000000000..67f6cf059 --- /dev/null +++ b/tests/neg/partialApplications.scala @@ -0,0 +1,11 @@ +object Test { + + type RMap[X, Y] = Map[Y, X] + val m = Map[Int, String]() + val ts: RMap[_, Int] = m // erorr // error + val us: RMap[String, _] = m // error // error + val vs: RMap[_, _] = m // error // error // error + val zz: RMap = m // error + +} + diff --git a/tests/pos/partialApplications.scala b/tests/pos/partialApplications.scala index c1df1dee2..285dc8661 100644 --- a/tests/pos/partialApplications.scala +++ b/tests/pos/partialApplications.scala @@ -8,6 +8,39 @@ object Test { val ys: StringlyHistogram[String] = xs - val zs: StringlyHistogram = xs + def e = xs + + val zs: StringlyHistogram[_] = e + + type IntMap[Y] = Map[Int, Y] + + val is = Map[Int, Boolean]() + + val js: IntMap[Boolean] = is + + val ks: IntMap[_] = is + + type RMap[X, Y] = Map[Y, X] + + val rs = Map[Int, Float]() + + val ss: RMap[Float, Int] = rs + +} + +object Test2 { + type Histogram = Map[_, Int] + + type StringlyHistogram = Histogram[_ >: String] // error + + val xs: Histogram[String] = Map[String, Int]() // error + + val ys: StringlyHistogram[String] = xs // error + + val zs: StringlyHistogram = xs // error + + val xs1 = xs + val ys1 = ys + val zs1 = zs } -- cgit v1.2.3