Skip to content

Reboot TypeGeneralizing to use SubtypingDiscoverer - #9263

Open
kripken wants to merge 25 commits into
WebAssembly:mainfrom
kripken:typegen
Open

kripken wants to merge 25 commits into
WebAssembly:mainfrom
kripken:typegen

Conversation

@kripken

@kripken kripken commented Oct 14, 2026

Copy link
Copy Markdown
Member

Now that we have a "late opts" stage, we can do things like generalize
types. This initial PR rewrites the pass to use the SubtypingDiscoverer
logic that was added since, and which makes the pass a lot
simpler (about half the length).

More work is needed to get this fuzzed etc., but just this rewrite
already removes some TODOs and asserts that are no longer relevant.
The pass can now run on a large Kotlin testcase without erroring, and
it shrinks the size there by 0.25% (not much yet, but after adding cast
generalization, perhaps more..?)

All old tests pass like before. (One new test is added, to handle a
crash that happened during this porting process.)

This is basically NFC, except that removing the old asserts mean
that more things can run.

@kripken
kripken requested a review from tlively October 14, 2026 22:10
@kripken
kripken requested a review from a team as a code owner October 14, 2026 22:10

@tlively tlively left a comment

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.

Some initial comments before I get into the meat of the new implementation.

Test coverage doesn't look great, especially for the new changes in type-generalizing.

  Metric       Covered / Total        Details
  ------------ ---------------------- ----------------------------------
  Lines        243 / 326 (74.5%)      Executable lines in diff
  Regions      195 / 232 (84.1%)      Sub-line / block AST regions in diff
  Branches     131 / 170 (77.1%)      True/False branch outcomes in diff
  MC/DC        6 / 21 (28.6%)         Condition independence pairs in diff
  Functions    28 / 103 (27.2%)       Modified functions called >= 1x

Comment thread src/ir/subtype-exprs.h
void visitRethrow(Rethrow* curr) {}
void visitThrowRef(ThrowRef* curr) {}
void visitThrowRef(ThrowRef* curr) {
self()->noteNonFlowSubtype(curr->exnref, Type(HeapType::exn, Nullable));

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.

Why "non-flow" here? The ref does flow into the throw and out of a later catch.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, but it doesn't flow out of this expression. Reading the docs on top, I guess this can be ambiguous. I can reword that?

Comment thread src/ir/subtype-exprs.h
if (curr->ref->type.isRef() && curr->ref->type.getHeapType().isShared()) {
share = Shared;
}
self()->noteNonFlowSubtype(

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.

Again, why non-flow? Previously the only use of noteNonFlowSubtype was for ref.eq. If all these new uses of noteNonFlowSubtype are correct, then I suspect there are many more places that should be using it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think this is correct (for the reasons I mentioned above) but yes, there might be more places it makes sense to use. I didn't try to find them all in this PR - would make more sense in a place where we can cover them more easily with tests, though I don't know that we have a better pass, actually...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I can remove the ones not directly needed for tests in this PR if you prefer, though e.g. the String ones seem obvious?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(Aside from them, the ThrowRef one can also be removed from this PR, it looks like.)

Comment thread src/ir/subtype-exprs.h
Comment on lines +356 to +360
if (curr->desc) {
auto descType = curr->type.getHeapType().getDescriptorType();
assert(descType);
self()->noteSubtype(curr->desc, Type(*descType, Nullable, Exact));
}

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.

If visitation order matters, this should come after visiting the fields. (And even if visitation order doesn't matter now, it would probably be better to be consistent about 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.

BrOn and RefCast are also missing descriptor handling.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The order doesn't matter, but I agree it is nice to follow. Fixed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

BrOn and RefCast are also missing descriptor handling.

How about leaving those for later + tests? This PR is mainly focused on the reboot, but not adding new functionality.

Comment thread test/lit/passes/type-generalizing.wast Outdated

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.

Is this change related?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes. The fuzzer uses SubtypingDiscoverer internally (to find mutations, of the proper types). So improvements here lead to fuzzer improvements.

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 probably worth a TODO about additional things that are not handled optimally by SubtypeDiscoverer because it assumes the result types cannot be generalized: specifically ref.as_non_null, br_on_null, publish, br_if, and maybe a couple others.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added a TODO

@tlively

tlively commented Oct 15, 2026

Copy link
Copy Markdown
Member

ChildTyper would probably be a better utility to use here because it does not assume the parent expression's type cannot be generalized.

(In fact, ChildTyper is a better fit for all current users of SubtypingDiscoverer except for Unsubtyping, which is where SubtypingDiscoverer originally came from.)

@tlively

tlively commented Oct 15, 2026

Copy link
Copy Markdown
Member

Actually, ChildTyper is not enough because it does not know about the required upper bounds on the parent expression's type, which means it cannot reflect those bounds in the constraints it reports for child types. We would need to build a full principal types utility (built on top of ChildTyper) to let us propagate type contraints from parents to children. I'll consider that as a follow-on project rather than holding up this PR.

@kripken

kripken commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Commit pushed with comprehensive tests for new functionality in this PR.

The one thing I did not do here is verify they are new and not previously covered (as I didn't write the old ones originally, so I'd need to do a lot of work to match things up). But I had Gemini check that. In the worst case, if it got it wrong, we might be adding some small amount of overlap.

@tlively

tlively commented Oct 16, 2026

Copy link
Copy Markdown
Member

Thanks, that sounds like the right trade off to me.

This branch has not been deployed

No deployments
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