Skip to content

Commit 63ce36a

Browse files
authored
Merge pull request #5885 from dotty-staging/add-immutable-array
Add an immutable array type
2 parents b69f6f7 + b9decdc commit 63ce36a

File tree

10 files changed

+323
-3
lines changed

10 files changed

+323
-3
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,8 @@ object Types {
11661166
else if (tp.symbol.isAliasType) tp.underlying.underlyingClassRef(refinementOK)
11671167
else NoType
11681168
case tp: AppliedType =>
1169-
tp.superType.underlyingClassRef(refinementOK)
1169+
if (tp.tycon.isLambdaSub) NoType
1170+
else tp.superType.underlyingClassRef(refinementOK)
11701171
case tp: AnnotatedType =>
11711172
tp.underlying.underlyingClassRef(refinementOK)
11721173
case tp: RefinedType =>

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1737,7 +1737,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
17371737
val app =
17381738
typed(untpd.Apply(core, untpd.TypedSplice(receiver) :: Nil), pt1, ctx.typerState.ownedVars)(
17391739
ctx.addMode(Mode.SynthesizeExtMethodReceiver))
1740-
if (!app.symbol.is(Extension))
1740+
val appSym =
1741+
app match {
1742+
case Inlined(call, _, _) => call.symbol
1743+
case _ => app.symbol
1744+
}
1745+
if (!appSym.is(Extension))
17411746
ctx.error(em"not an extension method: $methodRef", receiver.sourcePos)
17421747
app
17431748
}

compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ object VarianceChecker {
4848
case tref: TypeParamRef if tref.binder `eq` tl =>
4949
val v = tl.typeParams(tref.paramNum).paramVariance
5050
varianceConforms(variance, v) || { error(tref); false }
51+
case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedVarianceAnnot =>
52+
x
5153
case _ =>
5254
foldOver(x, t)
5355
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package scala
2+
import reflect.ClassTag
3+
4+
/** An immutable array. An `IArray[T]` has the same representation as an `Array[T]`,
5+
* but it cannot be updated. Unlike regular arrays, immutable arrays are covariant.
6+
*/
7+
opaque type IArray[+T] = Array[_ <: T]
8+
9+
object IArray {
10+
11+
/** Defines extension methods for immutable arrays */
12+
implied arrayOps {
13+
14+
/** The selection operation on an immutable array.
15+
*
16+
* @param arr the immutable array
17+
* @param n the index of the element to select
18+
* @return the element of the array at the given index
19+
*/
20+
inline def (arr: IArray[T]) apply[T] (n: Int): T = arr.asInstanceOf[Array[T]].apply(n)
21+
22+
/** The number of elements in an immutable array
23+
* @param arr the immutable array
24+
*/
25+
inline def (arr: IArray[T]) length[T] : Int = arr.asInstanceOf[Array[T]].length
26+
}
27+
28+
/** An immutable array of length 0.
29+
*/
30+
def empty[T: ClassTag]: IArray[T] = new Array[T](0)
31+
32+
/** An immutable array with given elements.
33+
*/
34+
def apply[T: ClassTag](xs: T*): IArray[T] = Array(xs: _*)
35+
def apply(x: Boolean, xs: Boolean*): IArray[Boolean] = Array(x, xs: _*)
36+
def apply(x: Byte, xs: Byte*): IArray[Byte] = Array(x, xs: _*)
37+
def apply(x: Short, xs: Short*): IArray[Short] = Array(x, xs: _*)
38+
def apply(x: Char, xs: Char*): IArray[Char] = Array(x, xs: _*)
39+
def apply(x: Int, xs: Int*): IArray[Int] = Array(x, xs: _*)
40+
def apply(x: Long, xs: Long*): IArray[Long] = Array(x, xs: _*)
41+
def apply(x: Float, xs: Float*): IArray[Float] = Array(x, xs: _*)
42+
def apply(x: Double, xs: Double*): IArray[Double] = Array(x, xs: _*)
43+
def apply(x: Unit, xs: Unit*): IArray[Unit] = Array(x, xs: _*)
44+
45+
/** Concatenates all arrays into a single immutable array.
46+
*
47+
* @param xss the given immutable arrays
48+
* @return the array created from concatenating `xss`
49+
*/
50+
def concat[T: ClassTag](xss: IArray[T]*): IArray[T] = Array.concat[T](xss.asInstanceOf[Seq[Array[T]]]: _*)
51+
52+
/** Returns an immutable array that contains the results of some element computation a number
53+
* of times. Each element is determined by a separate computation.
54+
*
55+
* @param n the number of elements in the array
56+
* @param elem the element computation
57+
*/
58+
def fill[T: ClassTag](n: Int)(elem: => T): IArray[T] =
59+
Array.fill(n)(elem)
60+
61+
/** Returns a two-dimensional immutable array that contains the results of some element computation a number
62+
* of times. Each element is determined by a separate computation.
63+
*
64+
* @param n1 the number of elements in the 1st dimension
65+
* @param n2 the number of elements in the 2nd dimension
66+
* @param elem the element computation
67+
*/
68+
def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): IArray[IArray[T]] =
69+
Array.fill(n1, n2)(elem)
70+
71+
/** Returns a three-dimensional immutable array that contains the results of some element computation a number
72+
* of times. Each element is determined by a separate computation.
73+
*
74+
* @param n1 the number of elements in the 1st dimension
75+
* @param n2 the number of elements in the 2nd dimension
76+
* @param n3 the number of elements in the 3nd dimension
77+
* @param elem the element computation
78+
*/
79+
def fill[T: ClassTag](n1: Int, n2: Int, n3: Int)(elem: => T): IArray[IArray[IArray[T]]] =
80+
Array.fill(n1, n2, n3)(elem)
81+
82+
/** Returns a four-dimensional immutable array that contains the results of some element computation a number
83+
* of times. Each element is determined by a separate computation.
84+
*
85+
* @param n1 the number of elements in the 1st dimension
86+
* @param n2 the number of elements in the 2nd dimension
87+
* @param n3 the number of elements in the 3nd dimension
88+
* @param n4 the number of elements in the 4th dimension
89+
* @param elem the element computation
90+
*/
91+
def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => T): IArray[IArray[IArray[IArray[T]]]] =
92+
Array.fill(n1, n2, n3, n4)(elem)
93+
94+
/** Returns a five-dimensional immutable array that contains the results of some element computation a number
95+
* of times. Each element is determined by a separate computation.
96+
*
97+
* @param n1 the number of elements in the 1st dimension
98+
* @param n2 the number of elements in the 2nd dimension
99+
* @param n3 the number of elements in the 3nd dimension
100+
* @param n4 the number of elements in the 4th dimension
101+
* @param n5 the number of elements in the 5th dimension
102+
* @param elem the element computation
103+
*/
104+
def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => T): IArray[IArray[IArray[IArray[IArray[T]]]]] =
105+
Array.fill(n1, n2, n3, n4, n5)(elem)
106+
107+
/** Returns an immutable array containing values of a given function over a range of integer
108+
* values starting from 0.
109+
*
110+
* @param n The number of elements in the array
111+
* @param f The function computing element values
112+
*/
113+
def tabulate[T: ClassTag](n: Int)(f: Int => T): IArray[T] =
114+
Array.tabulate(n)(f)
115+
116+
/** Returns a two-dimensional immutable array containing values of a given function
117+
* over ranges of integer values starting from `0`.
118+
*
119+
* @param n1 the number of elements in the 1st dimension
120+
* @param n2 the number of elements in the 2nd dimension
121+
* @param f The function computing element values
122+
*/
123+
def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): IArray[IArray[T]] =
124+
Array.tabulate(n1, n2)(f)
125+
126+
/** Returns a three-dimensional immutable array containing values of a given function
127+
* over ranges of integer values starting from `0`.
128+
*
129+
* @param n1 the number of elements in the 1st dimension
130+
* @param n2 the number of elements in the 2nd dimension
131+
* @param n3 the number of elements in the 3rd dimension
132+
* @param f The function computing element values
133+
*/
134+
def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => T): IArray[IArray[IArray[T]]] =
135+
Array.tabulate(n1, n2, n3)(f)
136+
137+
/** Returns a four-dimensional immutable array containing values of a given function
138+
* over ranges of integer values starting from `0`.
139+
*
140+
* @param n1 the number of elements in the 1st dimension
141+
* @param n2 the number of elements in the 2nd dimension
142+
* @param n3 the number of elements in the 3rd dimension
143+
* @param n4 the number of elements in the 4th dimension
144+
* @param f The function computing element values
145+
*/
146+
def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => T): IArray[IArray[IArray[IArray[T]]]] =
147+
Array.tabulate(n1, n2, n3, n4)(f)
148+
149+
/** Returns a five-dimensional immutable array containing values of a given function
150+
* over ranges of integer values starting from `0`.
151+
*
152+
* @param n1 the number of elements in the 1st dimension
153+
* @param n2 the number of elements in the 2nd dimension
154+
* @param n3 the number of elements in the 3rd dimension
155+
* @param n4 the number of elements in the 4th dimension
156+
* @param n5 the number of elements in the 5th dimension
157+
* @param f The function computing element values
158+
*/
159+
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]]]]] =
160+
Array.tabulate(n1, n2, n3, n4, n5)(f)
161+
162+
/** Returns an immutable array containing a sequence of increasing integers in a range.
163+
*
164+
* @param start the start value of the array
165+
* @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned)
166+
* @return the immutable array with values in range `start, start + 1, ..., end - 1`
167+
* up to, but excluding, `end`.
168+
*/
169+
def range(start: Int, end: Int): IArray[Int] = Array.range(start, end)
170+
171+
/** Returns an immutable array containing equally spaced values in some integer interval.
172+
*
173+
* @param start the start value of the array
174+
* @param end the end value of the array, exclusive (in other words, this is the first value '''not''' returned)
175+
* @param step the increment value of the array (may not be zero)
176+
* @return the immutable array with values in `start, start + step, ...` up to, but excluding `end`
177+
*/
178+
def range(start: Int, end: Int, step: Int): IArray[Int] = Array.range(start, end, step)
179+
180+
/** Returns an immutable array containing repeated applications of a function to a start value.
181+
*
182+
* @param start the start value of the array
183+
* @param len the number of elements returned by the array
184+
* @param f the function that is repeatedly applied
185+
* @return the immutable array returning `len` values in the sequence `start, f(start), f(f(start)), ...`
186+
*/
187+
def iterate[T: ClassTag](start: T, len: Int)(f: T => T): IArray[T] = Array.iterate(start, len)(f)
188+
189+
/** Returns a decomposition of the array into a sequence. This supports
190+
* a pattern match like `{ case IArray(x,y,z) => println('3 elements')}`.
191+
*
192+
* @param x the selector value
193+
* @return sequence wrapped in a [[scala.Some]], if `x` is a Seq, otherwise `None`
194+
*/
195+
def unapplySeq[T](x: IArray[T]) = Array.unapplySeq[T](x.asInstanceOf[Array[T]])
196+
}

tests/neg/alloc-abstract.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Test[T] {
2+
type U <: T
3+
4+
type Foo[T] = Array[T]
5+
6+
new T // error: not a class type
7+
new T() // error: not a class type
8+
new U // error: not a class type
9+
new U() // error: not a class type
10+
new IArray[String] // error: not a class type
11+
new IArray[String]() // error: not a class type
12+
new IArray[String](10) // error: not a class type // error: too mamy arguments
13+
14+
new Foo[String](10) // ok
15+
}

tests/neg/iarrays.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
object Test {
2+
3+
// Can't allocate an IArray
4+
new IArray[String](10) // error: not a class type // error: too many arguments
5+
6+
val xs = IArray(1, 2, 3)
7+
8+
// Can't have a wildcard IArray
9+
val ys: IArray[_] = xs
10+
11+
// Can't update an IArray
12+
xs(0) = 1 // error: value update is not a member
13+
xs(1) += 1 // error: value += is not a member
14+
15+
}

tests/neg/iarrays1.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object Test {
2+
3+
// Can't allocate an IArray
4+
5+
val xs = IArray(1, 2, 3)
6+
7+
// Can't have a wildcard IArray
8+
val ys: IArray[_] = xs // error: unreducible application
9+
10+
}

tests/neg/parser-stability-21.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
class x0[x1[]] // error
2-
extends x1[ // error
2+
extends x1[
33
// error

tests/pos/covariant-opaque.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import annotation.unchecked.uncheckedVariance
2+
3+
opaque type O[+T] = Array[T @uncheckedVariance]

tests/run/iarrays.scala

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import reflect.ClassTag
2+
object Test extends App {
3+
4+
val xs = IArray(1, 2, 3)
5+
6+
def f[T](ys: IArray[T]) = {
7+
assert(ys.length == 3)
8+
var sum = 0
9+
for (i <- 0 until ys.length)
10+
sum += xs(i)
11+
assert(sum == 6)
12+
}
13+
14+
f(xs)
15+
16+
var sum = 0
17+
for (i <- 0 until xs.length)
18+
sum += xs(i)
19+
assert(sum == 6)
20+
21+
def reduce[T](xs: IArray[T], z: T, op: (T, T) => T) = {
22+
var acc = z
23+
for (i <- 0 until xs.length)
24+
acc = op(acc, xs(i))
25+
acc
26+
}
27+
28+
def reduce2[T <: AnyRef](xs: IArray[T], z: T, op: (T, T) => T) = {
29+
var acc = z
30+
for (i <- 0 until xs.length)
31+
acc = op(acc, xs(i))
32+
acc
33+
}
34+
35+
def flatten[T: ClassTag](ys: IArray[IArray[T]]) = {
36+
var len = 0
37+
for (i <- 0 until ys.length) len += ys(i).length
38+
val flat = new Array[T](len)
39+
var k = 0
40+
for (i <- 0 until ys.length) {
41+
for (j <- 0 until ys(i).length) {
42+
flat(k) = ys(i)(j)
43+
k += 1
44+
}
45+
}
46+
IArray(flat: _*)
47+
}
48+
49+
val ys = IArray.concat(xs, xs, xs)
50+
assert(reduce(ys, 0, _ + _) == 18)
51+
52+
val ss = IArray("a", "b", "c")
53+
assert(reduce2(ss, "", _ ++ _) == "abc")
54+
55+
val zss = IArray.fill(2, 3)(1)
56+
val zs = flatten(zss)
57+
assert(reduce(zs, 0, _ + _) == 6)
58+
59+
val is = IArray.iterate(0, 4)(_ + 1)
60+
assert(reduce(is, 0, _ + _) == 6)
61+
62+
val IArray(1, 2, 3) = xs
63+
64+
val as: IArray[Any] = IArray(1, "hello")
65+
assert(as(as.length - 1) == "hello")
66+
assert(reduce(as, 0, (x, y) => x.toString ++ y.toString) == "01hello")
67+
68+
// Check that representation of IArray and Array is the same
69+
val bs: IArray[Double] = IArray(1.0, 2.0)
70+
val cs: Array[Double] = bs.asInstanceOf[Array[Double]]
71+
cs(1) = 3.0
72+
assert(bs(1) == 3.0)
73+
}

0 commit comments

Comments
 (0)