Skip to content

Do not simplify isInstanceOf if unrelated types might be subtypes at run-time#25535

Merged
SolalPirelli merged 13 commits into
scala:mainfrom
dotty-staging:solal/runtime-instanceof
Mar 26, 2026
Merged

Do not simplify isInstanceOf if unrelated types might be subtypes at run-time#25535
SolalPirelli merged 13 commits into
scala:mainfrom
dotty-staging:solal/runtime-instanceof

Conversation

@SolalPirelli

@SolalPirelli SolalPirelli commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Fixes #25037
Fixes #25097

Introduce typeMightBeSubtypeAtRuntime in Platform, and use it in TypeTestCasts to avoid simplifying type tests that may have different behavior at runtime on a specific platform.

Implement this method for the JVM, where everything is Serializable, and Scala.js, where integer semantics are a little funny due to JS's "everything is a double" model.

Drop the "a value type cannot be an instance of a reference type" error as it's wrong, 1.isInstanceOf[Object] is true, and move the neg test to a run test ensuring this.

How much have your relied on LLM-based tools in this contribution?

not

How was the solution tested?

new automated tests

@SolalPirelli SolalPirelli changed the title Do not simplify isInstanceOf if unrelated types might be subtypes at runtime Do not simplify isInstanceOf if unrelated types might be subtypes at run-time Mar 16, 2026
@SolalPirelli SolalPirelli requested a review from sjrd March 17, 2026 07:59
@SolalPirelli SolalPirelli marked this pull request as ready for review March 17, 2026 08:00
Comment thread compiler/src/dotty/tools/dotc/config/Platform.scala Outdated
Comment thread compiler/src/dotty/tools/dotc/config/SJSPlatform.scala Outdated

if foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass then
if foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass && !ctx.platform.typeMightSubtypeAtRuntime(foundEffectiveClass, testCls) then
report.error(em"cannot test if value of $exprType is a reference of $testCls", tree.srcPos)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change does not seem right. The platform should not influence whether code compiles or not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's 1 Scala.js test failing because I commented this out, I'll figure it out on Monday

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjrd I think this is a case of the compiler being a little over-eager with an error. This report could be a warning, there's nothing inherently broken with testing whether A is a subtype of B when we know it's unrelated. I pushed a commit with a comment explaining why we need to call the platform here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum. IMO in that case we must either
a) emit the compile error on JS as well, or
b) make it platform-dependent warning.

It makes no sense to have a platform-dependent compile error (unless there are interop features involved).

a) seems the most conservative here. IMO it's not nonsensical. One can argue that it's just a language rule that we forbid prim.isInstanceOf[NonPrim]. It doesn't need to have anything to do with the platform behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Unfortunately the way we get here is not by a direct check in the code but by another part of this file doing some widening (what else? :) ) so let me figure this out...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjrd so it turns out a few lines above there already was the infra to do basically (b), the unreachable function, so I used it.

However it's not strictly a platform-dependent warning because if we're in a pattern match we instead error with "unreachable case". Why that's an error and not a warning I don't know, but I don't think this PR should change that.

Comment thread tests/run/i1354.scala
@sjrd sjrd assigned SolalPirelli and unassigned sjrd Mar 20, 2026
@SolalPirelli SolalPirelli force-pushed the solal/runtime-instanceof branch from 447c0ef to f30fed2 Compare March 20, 2026 15:11
Comment thread project/Build.scala Outdated
lazy val sjsJUnitTests = project.in(file("tests/sjs-junit")).
enablePlugins(DottyJSPlugin).
dependsOn(`scala-library-sjs`).
dependsOn(`scala-library-sjs`, `scala3-compiler-bootstrapped`).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since these unit tests also depend on the compiler...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid that's wrong. That puts the compiler code on the classpath of sjsJUnitTests ! (also fixing this should be a separate PR anyway)

Comment thread project/Build.scala
dependsOn(`scala-library-sjs`, `scala3-compiler-bootstrapped`).
settings(
regularScalaJSProjectSettings,
bspEnabled := false,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the deleted file below, I changed it to a "warn" test since that aligns with how the compiler was handling other such issues

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub tip: you can comment on a whole file with the comment icon in the top right corner of a file's diff. This is true even for deleted files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat!

@SolalPirelli SolalPirelli force-pushed the solal/runtime-instanceof branch from 50cfe64 to 8f2b599 Compare March 25, 2026 08:00
@SolalPirelli SolalPirelli requested a review from sjrd March 25, 2026 10:34
Comment thread project/Build.scala Outdated
lazy val sjsJUnitTests = project.in(file("tests/sjs-junit")).
enablePlugins(DottyJSPlugin).
dependsOn(`scala-library-sjs`).
dependsOn(`scala-library-sjs`, `scala3-compiler-bootstrapped`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid that's wrong. That puts the compiler code on the classpath of sjsJUnitTests ! (also fixing this should be a separate PR anyway)

Comment thread tests/warn/typetest.check Outdated
-- Warning: tests/warn/typetest.scala:5:10 -----------------------------------------------------------------------------
5 | println(i.isInstanceOf[Object]) // warning
| ^
| this will always yield false since type Int is a primitive and class Object is a reference

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's going to say true if you actually run it, though, isn't it?

If it's meant as a progression test, say so in a comment on the test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it does say false, at least running on the JVM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that would then be a bug, because (1: Any).isInstanceOf[Object] definitely answers true today. It would be inconsistent for 1.isInstanceOf[Object] to return false.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to me thinking of : Any as a boxing operation, a value type instance isn't an instance of Object until you box it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Boxing is supposed to be transparent in Scala. There's no "boxing operation".

For example, you can still reproduce the result true without ever upcasting to Any:

scala> def test[T](x: T): Boolean = x.isInstanceOf[Object]; test[Int](5)
def test[T](x: T): Boolean
val res90: Boolean = true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.. could you say in the PR description what this PR changes? I wanted to take a quick look to understand, having a summary would really help.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do as soon as we agree on what that is 😂

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear this test currently fails to compile on main: https://github.com/scala/scala3/blob/main/tests/neg/typetest.scala

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was more of a general remark; you know what you changed, the thoughts behind it, the research you did. It saves reviewers a lot of time if you can summarize it in the PR description and commit message.

Comment thread project/Build.scala
dependsOn(`scala-library-sjs`, `scala3-compiler-bootstrapped`).
settings(
regularScalaJSProjectSettings,
bspEnabled := false,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub tip: you can comment on a whole file with the comment icon in the top right corner of a file's diff. This is true even for deleted files.

Comment thread compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala Outdated
@SolalPirelli

Copy link
Copy Markdown
Contributor Author

@sjrd I pushed a silly commit that comments out this warning/error entirely and... the only test that fails in the compiler is this typetest.scala that appears to be wrong today too. Should I properly delete the warning/error and figure out how to make 1.isInstanceOf[Object] return true as it should?

@sjrd

sjrd commented Mar 26, 2026

Copy link
Copy Markdown
Member

Let's do that, yes.

@SolalPirelli SolalPirelli force-pushed the solal/runtime-instanceof branch from fe36f98 to ed998a6 Compare March 26, 2026 13:55
@SolalPirelli SolalPirelli requested review from lrytz and sjrd March 26, 2026 15:32
@SolalPirelli SolalPirelli merged commit e0dd739 into scala:main Mar 26, 2026
64 checks passed
@SolalPirelli SolalPirelli deleted the solal/runtime-instanceof branch March 26, 2026 20:32
@WojciechMazur WojciechMazur added this to the 3.8.4 milestone Mar 31, 2026
tgodzik added a commit to scala/scala3-lts that referenced this pull request Apr 8, 2026
…run-time (scala#25535)

Fixes scala#25037
Fixes scala#25097

Introduce `typeMightBeSubtypeAtRuntime` in `Platform`, and use it in
`TypeTestCasts` to avoid simplifying type tests that may have different
behavior at runtime on a specific platform.

Implement this method for the JVM, where everything is `Serializable`,
and Scala.js, where integer semantics are a little funny due to JS's
"everything is a double" model.

Drop the "a value type cannot be an instance of a reference type" error
as it's wrong, `1.isInstanceOf[Object]` is `true`, and move the neg test
to a run test ensuring this.

## How much have your relied on LLM-based tools in this contribution?

not

## How was the solution tested?

new automated tests
[Cherry-picked e0dd739][modified]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrongly elided expression by the compiler (in Scala.js) Wrongly elided expression by the compiler

5 participants