diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 39a503cecee3..0f640f0a6b07 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1166,7 +1166,8 @@ object Types { else if (tp.symbol.isAliasType) tp.underlying.underlyingClassRef(refinementOK) else NoType case tp: AppliedType => - tp.superType.underlyingClassRef(refinementOK) + if (tp.tycon.isLambdaSub) NoType + else tp.superType.underlyingClassRef(refinementOK) case tp: AnnotatedType => tp.underlying.underlyingClassRef(refinementOK) case tp: RefinedType => diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index f513d2ba10d3..43ed31572723 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1737,7 +1737,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val app = typed(untpd.Apply(core, untpd.TypedSplice(receiver) :: Nil), pt1, ctx.typerState.ownedVars)( ctx.addMode(Mode.SynthesizeExtMethodReceiver)) - if (!app.symbol.is(Extension)) + val appSym = + app match { + case Inlined(call, _, _) => call.symbol + case _ => app.symbol + } + if (!appSym.is(Extension)) ctx.error(em"not an extension method: $methodRef", receiver.sourcePos) app } diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index 76be55f9cc9d..a36848d62c4b 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -48,6 +48,8 @@ object VarianceChecker { case tref: TypeParamRef if tref.binder `eq` tl => val v = tl.typeParams(tref.paramNum).paramVariance varianceConforms(variance, v) || { error(tref); false } + case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedVarianceAnnot => + x case _ => foldOver(x, t) } diff --git a/library/src-bootstrapped/scala/IArray.scala b/library/src-bootstrapped/scala/IArray.scala new file mode 100644 index 000000000000..7cae3d163fb0 --- /dev/null +++ b/library/src-bootstrapped/scala/IArray.scala @@ -0,0 +1,196 @@ +package scala +import reflect.ClassTag + +/** An immutable array. An `IArray[T]` has the same representation as an `Array[T]`, + * but it cannot be updated. Unlike regular arrays, immutable arrays are covariant. + */ +opaque type IArray[+T] = Array[_ <: T] + +object IArray { + + /** Defines extension methods for immutable arrays */ + implied arrayOps { + + /** The selection operation on an immutable array. + * + * @param arr the immutable array + * @param n the index of the element to select + * @return the element of the array at the given index + */ + inline def (arr: IArray[T]) apply[T] (n: Int): T = arr.asInstanceOf[Array[T]].apply(n) + + /** The number of elements in an immutable array + * @param arr the immutable array + */ + inline def (arr: IArray[T]) length[T] : Int = arr.asInstanceOf[Array[T]].length + } + + /** An immutable array of length 0. + */ + def empty[T: ClassTag]: IArray[T] = new Array[T](0) + + /** An immutable array with given elements. + */ + def apply[T: ClassTag](xs: T*): IArray[T] = Array(xs: _*) + def apply(x: Boolean, xs: Boolean*): IArray[Boolean] = Array(x, xs: _*) + def apply(x: Byte, xs: Byte*): IArray[Byte] = Array(x, xs: _*) + def apply(x: Short, xs: Short*): IArray[Short] = Array(x, xs: _*) + def apply(x: Char, xs: Char*): IArray[Char] = Array(x, xs: _*) + def apply(x: Int, xs: Int*): IArray[Int] = Array(x, xs: _*) + def apply(x: Long, xs: Long*): IArray[Long] = Array(x, xs: _*) + def apply(x: Float, xs: Float*): IArray[Float] = Array(x, xs: _*) + def apply(x: Double, xs: Double*): IArray[Double] = Array(x, xs: _*) + def apply(x: Unit, xs: Unit*): IArray[Unit] = Array(x, xs: _*) + + /** Concatenates all arrays into a single immutable array. + * + * @param xss the given immutable arrays + * @return the array created from concatenating `xss` + */ + def concat[T: ClassTag](xss: IArray[T]*): IArray[T] = Array.concat[T](xss.asInstanceOf[Seq[Array[T]]]: _*) + + /** Returns an immutable array that contains the results of some element computation a number + * of times. Each element is determined by a separate computation. + * + * @param n the number of elements in the array + * @param elem the element computation + */ + def fill[T: ClassTag](n: Int)(elem: => T): IArray[T] = + Array.fill(n)(elem) + + /** Returns a two-dimensional immutable array that contains the results of some element computation a number + * of times. Each element is determined by a separate computation. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): IArray[IArray[T]] = + Array.fill(n1, n2)(elem) + + /** Returns a three-dimensional immutable array that contains the results of some element computation a number + * of times. Each element is determined by a separate computation. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3nd dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int)(elem: => T): IArray[IArray[IArray[T]]] = + Array.fill(n1, n2, n3)(elem) + + /** Returns a four-dimensional immutable array that contains the results of some element computation a number + * of times. Each element is determined by a separate computation. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3nd dimension + * @param n4 the number of elements in the 4th dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => T): IArray[IArray[IArray[IArray[T]]]] = + Array.fill(n1, n2, n3, n4)(elem) + + /** Returns a five-dimensional immutable array that contains the results of some element computation a number + * of times. Each element is determined by a separate computation. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3nd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param elem the element computation + */ + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => T): IArray[IArray[IArray[IArray[IArray[T]]]]] = + Array.fill(n1, n2, n3, n4, n5)(elem) + + /** Returns an immutable array containing values of a given function over a range of integer + * values starting from 0. + * + * @param n The number of elements in the array + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n: Int)(f: Int => T): IArray[T] = + Array.tabulate(n)(f) + + /** Returns a two-dimensional immutable array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): IArray[IArray[T]] = + Array.tabulate(n1, n2)(f) + + /** Returns a three-dimensional immutable array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => T): IArray[IArray[IArray[T]]] = + Array.tabulate(n1, n2, n3)(f) + + /** Returns a four-dimensional immutable array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => T): IArray[IArray[IArray[IArray[T]]]] = + Array.tabulate(n1, n2, n3, n4)(f) + + /** Returns a five-dimensional immutable array containing values of a given function + * over ranges of integer values starting from `0`. + * + * @param n1 the number of elements in the 1st dimension + * @param n2 the number of elements in the 2nd dimension + * @param n3 the number of elements in the 3rd dimension + * @param n4 the number of elements in the 4th dimension + * @param n5 the number of elements in the 5th dimension + * @param f The function computing element values + */ + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => T): IArray[IArray[IArray[IArray[IArray[T]]]]] = + Array.tabulate(n1, n2, n3, n4, n5)(f) + + /** Returns an immutable array containing a sequence of increasing integers in a range. + * + * @param start the start value of the array + * @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned) + * @return the immutable array with values in range `start, start + 1, ..., end - 1` + * up to, but excluding, `end`. + */ + def range(start: Int, end: Int): IArray[Int] = Array.range(start, end) + + /** Returns an immutable array containing equally spaced values in some integer interval. + * + * @param start the start value of the array + * @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned) + * @param step the increment value of the array (may not be zero) + * @return the immutable array with values in `start, start + step, ...` up to, but excluding `end` + */ + def range(start: Int, end: Int, step: Int): IArray[Int] = Array.range(start, end, step) + + /** Returns an immutable array containing repeated applications of a function to a start value. + * + * @param start the start value of the array + * @param len the number of elements returned by the array + * @param f the function that is repeatedly applied + * @return the immutable array returning `len` values in the sequence `start, f(start), f(f(start)), ...` + */ + def iterate[T: ClassTag](start: T, len: Int)(f: T => T): IArray[T] = Array.iterate(start, len)(f) + + /** Returns a decomposition of the array into a sequence. This supports + * a pattern match like `{ case IArray(x,y,z) => println('3 elements')}`. + * + * @param x the selector value + * @return sequence wrapped in a [[scala.Some]], if `x` is a Seq, otherwise `None` + */ + def unapplySeq[T](x: IArray[T]) = Array.unapplySeq[T](x.asInstanceOf[Array[T]]) +} \ No newline at end of file diff --git a/tests/neg/alloc-abstract.scala b/tests/neg/alloc-abstract.scala new file mode 100644 index 000000000000..e0bcef133d67 --- /dev/null +++ b/tests/neg/alloc-abstract.scala @@ -0,0 +1,15 @@ +class Test[T] { + type U <: T + + type Foo[T] = Array[T] + + new T // error: not a class type + new T() // error: not a class type + new U // error: not a class type + new U() // error: not a class type + new IArray[String] // error: not a class type + new IArray[String]() // error: not a class type + new IArray[String](10) // error: not a class type // error: too mamy arguments + + new Foo[String](10) // ok +} \ No newline at end of file diff --git a/tests/neg/iarrays.scala b/tests/neg/iarrays.scala new file mode 100644 index 000000000000..88e77085972d --- /dev/null +++ b/tests/neg/iarrays.scala @@ -0,0 +1,15 @@ +object Test { + + // Can't allocate an IArray + new IArray[String](10) // error: not a class type // error: too many arguments + + val xs = IArray(1, 2, 3) + + // Can't have a wildcard IArray + val ys: IArray[_] = xs + + // Can't update an IArray + xs(0) = 1 // error: value update is not a member + xs(1) += 1 // error: value += is not a member + +} \ No newline at end of file diff --git a/tests/neg/iarrays1.scala b/tests/neg/iarrays1.scala new file mode 100644 index 000000000000..9c38c2452618 --- /dev/null +++ b/tests/neg/iarrays1.scala @@ -0,0 +1,10 @@ +object Test { + + // Can't allocate an IArray + + val xs = IArray(1, 2, 3) + + // Can't have a wildcard IArray + val ys: IArray[_] = xs // error: unreducible application + +} \ No newline at end of file diff --git a/tests/neg/parser-stability-21.scala b/tests/neg/parser-stability-21.scala index 908e2b9bb12f..350e80caae2c 100644 --- a/tests/neg/parser-stability-21.scala +++ b/tests/neg/parser-stability-21.scala @@ -1,3 +1,3 @@ class x0[x1[]] // error - extends x1[ // error + extends x1[ // error \ No newline at end of file diff --git a/tests/pos/covariant-opaque.scala b/tests/pos/covariant-opaque.scala new file mode 100644 index 000000000000..9a9e0328ced9 --- /dev/null +++ b/tests/pos/covariant-opaque.scala @@ -0,0 +1,3 @@ +import annotation.unchecked.uncheckedVariance + +opaque type O[+T] = Array[T @uncheckedVariance] diff --git a/tests/run/iarrays.scala b/tests/run/iarrays.scala new file mode 100644 index 000000000000..26a189c08eaa --- /dev/null +++ b/tests/run/iarrays.scala @@ -0,0 +1,73 @@ +import reflect.ClassTag +object Test extends App { + + val xs = IArray(1, 2, 3) + + def f[T](ys: IArray[T]) = { + assert(ys.length == 3) + var sum = 0 + for (i <- 0 until ys.length) + sum += xs(i) + assert(sum == 6) + } + + f(xs) + + var sum = 0 + for (i <- 0 until xs.length) + sum += xs(i) + assert(sum == 6) + + def reduce[T](xs: IArray[T], z: T, op: (T, T) => T) = { + var acc = z + for (i <- 0 until xs.length) + acc = op(acc, xs(i)) + acc + } + + def reduce2[T <: AnyRef](xs: IArray[T], z: T, op: (T, T) => T) = { + var acc = z + for (i <- 0 until xs.length) + acc = op(acc, xs(i)) + acc + } + + def flatten[T: ClassTag](ys: IArray[IArray[T]]) = { + var len = 0 + for (i <- 0 until ys.length) len += ys(i).length + val flat = new Array[T](len) + var k = 0 + for (i <- 0 until ys.length) { + for (j <- 0 until ys(i).length) { + flat(k) = ys(i)(j) + k += 1 + } + } + IArray(flat: _*) + } + + val ys = IArray.concat(xs, xs, xs) + assert(reduce(ys, 0, _ + _) == 18) + + val ss = IArray("a", "b", "c") + assert(reduce2(ss, "", _ ++ _) == "abc") + + val zss = IArray.fill(2, 3)(1) + val zs = flatten(zss) + assert(reduce(zs, 0, _ + _) == 6) + + val is = IArray.iterate(0, 4)(_ + 1) + assert(reduce(is, 0, _ + _) == 6) + + val IArray(1, 2, 3) = xs + + val as: IArray[Any] = IArray(1, "hello") + assert(as(as.length - 1) == "hello") + assert(reduce(as, 0, (x, y) => x.toString ++ y.toString) == "01hello") + + // Check that representation of IArray and Array is the same + val bs: IArray[Double] = IArray(1.0, 2.0) + val cs: Array[Double] = bs.asInstanceOf[Array[Double]] + cs(1) = 3.0 + assert(bs(1) == 3.0) +} \ No newline at end of file