aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala29
-rw-r--r--test/dotc/tests.scala1
-rw-r--r--tests/neg/partialApplications.scala11
-rw-r--r--tests/pos/partialApplications.scala35
4 files changed, 74 insertions, 2 deletions
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
}