8000 Merge pull request #5051 from gzm0/fix-array-types · scala-js/scala-js@ae0946a · GitHub
[go: up one dir, main page]

Skip to content

Commit ae0946a

Browse files
authored
Merge pull request #5051 from gzm0/fix-array-types
IRChecker: Fix type-checking of Array operations
2 parents 727f437 + 40f7ff5 commit ae0946a

File tree

2 files changed

+89
-2
lines changed

2 files changed

+89
-2
lines changed

linker/shared/src/main/scala/org/scalajs/linker/checker/IRChecker.scala

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,37 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter) {
253253
_:JSSuperSelect | _:JSGlobalRef =>
254254
}
255255
typecheckExpr(lhs, env)
256-
typecheckExpect(rhs, env, lhs.tpe)
256+
257+
val expectedRhsTpe = lhs match {
258+
case ArraySelect(array, _) =>
259+
/* Array assignments are unsound due to covariance of arrays
260+
* To maintain the subtyping relationship in the IR, we have to
261+
* allow assignments of any type to
262+
* - Array[Object] (that's expected)
263+
* - and its subtypes (that's not expected)
264+
*/
265+
266+
array.tpe match {
267+
case ArrayType(ArrayTypeRef(PrimRef(tpe), 1), _) =>
268+
// for primitive arrays, only allow assignment of that primitive.
269+
tpe
270+
271+
case _ =>
272+
/* All other types are either
273+
* - Subtypes of Array[Object] (including null / nothing)
274+
* Recall: `Array[Array[A]] <: Array[Object]` even for primitive `A`.
275+
* - Ill typed IR
276+
* (in which case typechecking the lhs above will emit an error).
10000 277+
*
278+
* Allow any rhs.
279+
*/
280+
AnyType
281+
}
282+
283+
case _ => lhs.tpe
284+
}
285+
286+
typecheckExpect(rhs, env, expectedRhsTpe)
257287

258288
case Return(expr, label) =>
259289
val returnType = env.returnTypes(label.name)
@@ -499,13 +529,17 @@ private final class IRChecker(unit: LinkingUnit, reporter: ErrorReporter) {
499529

500530
case ArrayLength(array) =>
501531
typecheckExpr(array, env)
502-
if (!array.tpe.isInstanceOf[ArrayType])
532+
if (array.tpe != NullType &&
533+
array.tpe != NothingType &&
534+
!array.tpe.isInstanceOf[ArrayType])
503535
reportError(i"Array type expected but ${array.tpe} found")
504536

505537
case ArraySelect(array, index) =>
506538
typecheckExpect(index, env, IntType)
507539
typecheckExpr(array, env)
508540
array.tpe match {
541+
case NothingType => // ok
542+
case NullType => // will NPE, but allowed.
509543
case arrayType: ArrayType =>
510544
if (tree.tpe != arrayElemType(arrayType))
511545
reportError(i"Array select of array type $arrayType typed as ${tree.tpe}")

linker/shared/src/test/scala/org/scalajs/linker/IRCheckerTest.scala

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,59 @@ class IRCheckerTest {
253253
}
254254
}
255255

256+
@Test
257+
def arrayOpsNullOrNothing(): AsyncResult = await {
258+
val classDefs = Seq(
259+
mainTestClassDef(
260+
Block(
261+
ArraySelect(Null(), int(1))(NothingType),
262+
ArrayLength(Null()),
263+
ArraySelect(Throw(Null()), int(1))(NothingType),
264+
ArrayLength(Throw(Null()))
265+
)
266+
)
267+
)
268+
269+
testLinkNoIRError(classDefs, MainTestModuleInitializers)
270+
}
271+
272+
@Test
273+
def arrayAssignCovariant(): AsyncResult = await {
274+
val classDefs = Seq(
275+
classDef("Foo", superClass = Some(ObjectClass)),
276+
mainTestClassDef(
277+
Assign(
278+
ArraySelect(
279+
ArrayValue(ArrayTypeRef.of(ClassRef("Foo")), Nil),
280+
int(1)
281+
)(ClassType("Foo", true)),
282+
int(1) // not a Foo, but OK.
283+
)
284+
)
285+
)
286+
287+
testLinkNoIRError(classDefs, MainTestModuleInitializers)
288+
}
289+
290+
@Test
291+
def arrayNoAssignCovariantPrimitive(): AsyncResult = await {
292+
val classDefs = Seq(
293+
mainTestClassDef(
294+
Assign(
295+
ArraySelect(
296+
ArrayValue(ArrayTypeRef.of(IntRef), Nil),
297+
int(1)
298+
)(IntType),
299+
str("foo")
300+
)
301+
)
302+
)
303+
304+
for (log <- testLinkIRErrors(classDefs, MainTestModuleInitializers)) yield {
305+
log.assertContainsError("int expected but string found for tree of type")
306+
}
307+
}
308+
256309
}
257310

258311
object IRCheckerTest {

0 commit comments

Comments
 (0)
0