-
Notifications
You must be signed in to change notification settings - Fork 0
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
Exact abstract types #15
Comments
The operation you're describing here is not a plain union (or lattice join) operation because it does not maintain key properties such as Since you care about maximizing your optimizing power, I would expect you to prefer allowing more uninhabitable types, though. If, through some sequence of optimizations, wasm-opt produces a block (for example) with result type That being said, we could still disallow exact references to abstract heap types if we wanted to. To keep each type hierarchy a lattice, we would need to special-case I would expect that Binaryen would continue to internally allow all exact references for the additional follow-on optimization power in case any optimizations are able to produce an exact reference to an abstract type. We would just erase the exactness as necessary in our binary writer.
I would strongly prefer we not do this. Binaryen depends on being able to make non-nullable locals nullable as a fixup whenever an optimization changes control flow so a
I know this is tongue-in-cheek, but I strongly believe that targeted property fuzzing of the type system implementation can help avoid this, no matter what particular rules we choose for typing. |
OTOH, Binaryen can and should do the I can see two options:
I would probably lean toward the second. If we ever allow exact references to abstract heap types in the future, @jakobkummerow, does one of those options sound nicer to you? @rossberg, what would you think of this change? |
Hm, my vague concern with disallowing exact abstract types is that it appears to introduce a discontinuity in the type algebra. Those generally have a strong tendency to mess things up. Maybe that's not a problem in this particular case, at least I can't think of an immediate issue, but I wouldn't bet on it either. Perhaps it makes sense if we view the Note however that uninhabited types are a pretty normal thing in any sufficiently expressive type system, and cannot generally be avoided. Ruling them out just for its own sake is a weak motivation. |
Thinking through this some more, I’ve just convinced myself that making it part of heap types actually is the right way to skin the game. That is, instead of extending the syntax of reference types, we merely
Relative to Wasm 3.0 nothing else changes. In particular, nothing new needs to be specified for the bottom type, since the existing rule The consequence of course is that we now need to allow |
Sounds good to me. @rossberg, are you thinking that we need encodings for both |
@tlively, |
Oh, I see. Currently in the binary format we have |
#18 implements this change. PTAL! |
We can consider this fixed by #18. Thanks! |
I'd like to reopen the discussion about exact abstract types (such as
(ref null? exact any)
).Motivation: I've thought about computing type unions today. By that I mean specifically: when an optimizing compiler encounters a control flow merge, it'll want to compute the type of any merged values as accurately as possible, because having an accurate type might unlock further optimization opportunities in the subsequent code (such as: eliminating casts and branches), whereas on the flip side having inaccurate, overly-generic types might rule out optimizations that would otherwise be applicable. So, just to be extra clear about it, I'm not talking about spec-level union types. I'm aware that as far as the spec is concerned, any control flow merge point will have explicit type annotations, and a validator only needs to check whether the "incoming" types are subtypes of the merged type. But these merged types are often rather loose upper bounds for the values that will actually be seen there, and optimizing engines want to do better.
I'm also well aware that folks tend to say "we must have regularity!!!" as a reflex. In this particular case, I think the "regularity" idea is that the nullability, exactness, and heaptype dimensions of a reference type can be considered orthogonally to each other, so the hope is that if each of these is "regular" on its own, then their composition will also be "regular". On closer inspection, that is not the case: uninhabited types are noise/pollution, and having to filter them out makes implementations overly complex and irregular.
To illustrate, I think this is the behavior an optimizing compiler would want to implement for computing type unions at control flow merge points (assuming a module that has types
$sub <: $super <: eq
):Note in particular the lines annotated with
[!!!]
, indicating results that may be surprising. I find them surprising too: they violate the rule thatunion(a, b) == b if a <: b
, at least in the heaptype dimension. But we wouldn't want the union of(ref null none)
and(ref exact any)
to be(ref null any)
: that would destroy the highly valuable information that the value described by this type is guaranteed to always benull
(on all reachable control flow paths)!Then note that these
[!!!]
lines are precisely those whereexact eq
occurs as an input.This is a new issue with this proposal because previously, only none/bottom types were uninhabited, so handling them fit well into algorithms because they were subtypes of everything else anyway. But now we have "uninhabited supertypes", which are just weird.
Obviously, the behavior above can be implemented, and after shipping a few security vulnerabilities caused by type confusions to their users, most implementations will probably even get it right eventually. But what's the benefit? Previous conversations have agreed that uninhabitable exact abstract types are useless. They may be harmless on the spec level, but they're harmful for implementations.
Spec regularity causes implementation irregularity in this case.
I'm sympathetic to the desire to keep the spec simple. But a few restrictions can hardly be considered excessive complexity. And they're not painting us into a corner either: restrictions can always be lifted if a need for that is discovered.
So, I have two suggestions:
(1) Let's please allow
exact
only for indexed reference types. IOW,(ref null? exact X)
only validates ifX
is a type index.(2) Ideally we'd even go one step further and also disallow the combination
null
+exact
, since it's a bit of a contradiction (if the thing with type(ref null exact $x)
can benull
, then it's not "exactly$x
"), and not needed for the primary use case. The recent discussion about shorthands already hinted in the direction thatnull exact
is likely to be far less useful in practice than non-nullableexact
. Perhaps we can get by without it entirely? That would turn the two dimensions nullability×exactness into a 3-level scale: in increasing order of strictness, a reference can be nullable, non-nullable, or exact.The text was updated successfully, but these errors were encountered: