Skip to content

Removes spurious js.AsInstanceOf around the js.LinkTimeIf #5210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 18, 2025

Conversation

tanishiking
Copy link
Contributor

Cherry-picking a change from #5168 (comment) :)

When B<:A and C<:A, linkTimeIf[A](cond){ B }{ C } would generate an .asInstanceOf[A] that casts the resulting value to a supertype.
However, this cast is redundant,
as the linker will prune one branch at linktime,
and the remaining expression is already known to be a compatible type.

This commit eliminates the surrounding .asInstanceOf around the linkTimeIf[T] when both thenp and elsep are surely subtypes of T.

context: #5168

case Apply(fun, List(cond, thenp, elsep))
if fun.symbol == jsDefinitions.LinkingInfo_linkTimeIf &&
thenp.tpe <:< targetTpe && elsep.tpe <:< targetTpe =>
val genObj = genExpr(obj).asInstanceOf[js.LinkTimeIf]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The original change already looks good to me, but maybe we can match against genExpr(obj) and abort if it's not typed js.LinkTimeIf? That would display informative error when the compiler generates an unexpected IR by accident.

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, that would provide more context (especially with a message that includes the current position) in case something fails in the future.

Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

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

IMO we should also have a test in the compiler tests' OptimizationTest. I think the "impl pattern" should be tested there, with a hasNot assertion that makes sure there is no js.AsInstanceOf in the produced code.

case Apply(fun, List(cond, thenp, elsep))
if fun.symbol == jsDefinitions.LinkingInfo_linkTimeIf &&
thenp.tpe <:< targetTpe && elsep.tpe <:< targetTpe =>
val genObj = genExpr(obj).asInstanceOf[js.LinkTimeIf]
Copy link
Member

Choose a reason for hiding this comment

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

Indeed, that would provide more context (especially with a message that includes the current position) in case something fails in the future.

Comment on lines 3212 to 3220
/* This is an optimization for `linkTimeIf(cond)(thenp)(elsep).asInstanceOf[T]`.
* If both `thenp` and `elsep` are subtypes of `T`, the `asInstanceOf`
* is redundant and can be removed.
*/
Copy link
Member

Choose a reason for hiding this comment

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

Let's elaborate a bit more on why this is useful:

Suggested change
/* This is an optimization for `linkTimeIf(cond)(thenp)(elsep).asInstanceOf[T]`.
* If both `thenp` and `elsep` are subtypes of `T`, the `asInstanceOf`
* is redundant and can be removed.
*/
/* This is an optimization for `linkTimeIf(cond)(thenp)(elsep).asInstanceOf[T]`.
* If both `thenp` and `elsep` are subtypes of `T`, the `asInstanceOf`
* is redundant and can be removed. The optimizer already routinely performs
* this optimization. However, that comes too late for the module instance
* field alias analysis performed by `IncOptimizer`. In that case, while the
* desugarer removes the `LinkTimeIf`, the extra `AsInstanceOf` prevents
* aliasing the field. Removing the cast ahead of time in the compiler allows
* field aliases to be recognized in the presence of `LinkTimeIf`s.
*/

}

@Test def implPattern(): Unit = {
sealed abstract class ArrayImpl {
Copy link
Member

Choose a reason for hiding this comment

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

This abstract class and its two objects should static for the pattern to be the one we are interested in. You can put them in the companion object of LinkTimeIfTest. We often do that.

When B<:A and C<:A, `linkTimeIf[A](cond){ B }{ C }` would
generate an `.asInstanceOf[A]` that casts the resulting
value to a supertype.
However, this cast is redundant,
as the linker will prune one branch at linktime,
and the remaining expression is already known to be a compatible type.

This commit eliminates the surrounding `.asInstanceOf` around the
`linkTimeIf[T]` when both `thenp` and `elsep` are surely subtypes of `T`.
The optimizer already routinely performs
this optimization. However, that comes too late for the module instance
field alias analysis performed by `IncOptimizer`. In that case, while the
desugarer removes the `LinkTimeIf`, the extra `AsInstanceOf` prevents
aliasing the field. Removing the cast ahead of time in the compiler allows
field aliases to be recognized in the presence of `LinkTimeIf`s.

context: scala-js#5168

Co-authored-by: Sébastien Doeraene <sjrdoeraene@gmail.com>
@tanishiking tanishiking force-pushed the asinstanceof-linktimeif branch from a3a0b43 to 9bb267c Compare July 18, 2025 08:30
@sjrd sjrd enabled auto-merge July 18, 2025 09:12
@sjrd sjrd merged commit 1501c94 into scala-js:main Jul 18, 2025
3 checks passed
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.

2 participants