From 80d986997e58b5195654b6f9ca8cd81bc62f4bbf Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 26 May 2012 17:26:29 +0200 Subject: SI-5683 Fail gracefully when transposing a ragged type arg matrix. The code used to do this, until `transpose` starting throwing IAE rather than AIOOBE. Symptomatic treatment only: The reported crasher now infers ill-kinded type args and reports an error. --- src/compiler/scala/reflect/internal/Types.scala | 61 ++++++++++++---------- .../scala/reflect/internal/util/Collections.scala | 6 +++ test/files/neg/t5683.check | 16 ++++++ test/files/neg/t5683.scala | 23 ++++++++ 4 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 test/files/neg/t5683.check create mode 100644 test/files/neg/t5683.scala diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index 67c858356c..02e11ddd59 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -6572,47 +6572,50 @@ trait Types extends api.Types { self: SymbolTable => else Some(typeRef(pre, sym, List(lub(args)))) } } - else { - val args = map2(sym.typeParams, argss.transpose) { (tparam, as) => - if (depth == 0) { - if (tparam.variance == variance) { - // Take the intersection of the upper bounds of the type parameters - // rather than falling all the way back to "Any", otherwise we end up not - // conforming to bounds. - val bounds0 = sym.typeParams map (_.info.bounds.hi) filterNot (_.typeSymbol == AnyClass) - if (bounds0.isEmpty) AnyClass.tpe - else intersectionType(bounds0 map (b => b.asSeenFrom(tps.head, sym))) + else transposeSafe(argss) match { + case None => + // transpose freaked out because of irregular argss + // catching just in case (shouldn't happen, but also doesn't cost us) + // [JZ] It happens: see SI-5683. + debuglog("transposed irregular matrix!?" +(tps, argss)) + None + case Some(argsst) => + val args = map2(sym.typeParams, argsst) { (tparam, as) => + if (depth == 0) { + if (tparam.variance == variance) { + // Take the intersection of the upper bounds of the type parameters + // rather than falling all the way back to "Any", otherwise we end up not + // conforming to bounds. + val bounds0 = sym.typeParams map (_.info.bounds.hi) filterNot (_.typeSymbol == AnyClass) + if (bounds0.isEmpty) AnyClass.tpe + else intersectionType(bounds0 map (b => b.asSeenFrom(tps.head, sym))) + } + else if (tparam.variance == -variance) NothingClass.tpe + else NoType } - else if (tparam.variance == -variance) NothingClass.tpe - else NoType - } - else { - if (tparam.variance == variance) lub(as, decr(depth)) - else if (tparam.variance == -variance) glb(as, decr(depth)) else { - val l = lub(as, decr(depth)) - val g = glb(as, decr(depth)) - if (l <:< g) l + if (tparam.variance == variance) lub(as, decr(depth)) + else if (tparam.variance == -variance) glb(as, decr(depth)) + else { + val l = lub(as, decr(depth)) + val g = glb(as, decr(depth)) + if (l <:< g) l else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we // just err on the conservative side, i.e. with a bound that is too high. // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251 - val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l) - capturedParams += qvar - qvar.tpe + val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l) + capturedParams += qvar + qvar.tpe + } } } } - } - if (args contains NoType) None - else Some(existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))) + if (args contains NoType) None + else Some(existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))) } } catch { case ex: MalformedType => None - case ex: IndexOutOfBoundsException => // transpose freaked out because of irregular argss - // catching just in case (shouldn't happen, but also doesn't cost us) - debuglog("transposed irregular matrix!?"+ (tps, argss)) - None } case SingleType(_, sym) :: rest => val pres = tps map (_.prefix) diff --git a/src/compiler/scala/reflect/internal/util/Collections.scala b/src/compiler/scala/reflect/internal/util/Collections.scala index 2e119f8ccc..1f8eb15c90 100644 --- a/src/compiler/scala/reflect/internal/util/Collections.scala +++ b/src/compiler/scala/reflect/internal/util/Collections.scala @@ -201,6 +201,12 @@ trait Collections { } true } + + final def transposeSafe[A](ass: List[List[A]]): Option[List[List[A]]] = try { + Some(ass.transpose) + } catch { + case _: IllegalArgumentException => None + } } object Collections extends Collections { } diff --git a/test/files/neg/t5683.check b/test/files/neg/t5683.check new file mode 100644 index 0000000000..7c0e50113c --- /dev/null +++ b/test/files/neg/t5683.check @@ -0,0 +1,16 @@ +t5683.scala:12: error: inferred kinds of the type arguments (Object,Int) do not conform to the expected kinds of the type parameters (type M,type B). +Object's type parameters do not match type M's expected parameters: +class Object has no type parameters, but type M has one + val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] } + ^ +t5683.scala:12: error: type mismatch; + found : Int => Test.W[String,Int] + required: Int => M[B] + val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] } + ^ +t5683.scala:12: error: type mismatch; + found : Test.K[M,Int,B] + required: Test.K[Test.StringW,Int,Int] + val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] } + ^ +three errors found diff --git a/test/files/neg/t5683.scala b/test/files/neg/t5683.scala new file mode 100644 index 0000000000..05ab035792 --- /dev/null +++ b/test/files/neg/t5683.scala @@ -0,0 +1,23 @@ +object Test { + trait NT[X] + trait W[W, A] extends NT[Int] + type StringW[T] = W[String, T] + trait K[M[_], A, B] + + def k[M[_], B](f: Int => M[B]): K[M, Int, B] = null + + val okay1: K[StringW,Int,Int] = k{ (y: Int) => null: StringW[Int] } + val okay2 = k[StringW,Int]{ (y: Int) => null: W[String, Int] } + + val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] } + + // remove `extends NT[Int]`, and the last line gives an inference error + // rather than a crash. + // test/files/pos/t5683.scala:12: error: no type parameters for method k: (f: Int => M[B])Test.K[M,Int,B] exist so that it can be applied to arguments (Int => Test.W[String,Int]) + // --- because --- + // argument expression's type is not compatible with formal parameter type; + // found : Int => Test.W[String,Int] + // required: Int => ?M[?B] + // val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] } + // ^ +} -- cgit v1.2.3