Skip to content

FuzzAgainstJS: Do not refine exact to inexact defined types#8855

Merged
kripken merged 5 commits into
WebAssembly:mainfrom
kripken:fuzz.exact
Jun 18, 2026
Merged

FuzzAgainstJS: Do not refine exact to inexact defined types#8855
kripken merged 5 commits into
WebAssembly:mainfrom
kripken:fuzz.exact

Conversation

@kripken

@kripken kripken commented Jun 17, 2026

Copy link
Copy Markdown
Member

It is ok to refine exact $A to nullref, but not to inexact $B.

@kripken kripken requested a review from tlively June 17, 2026 21:03
@kripken kripken requested a review from a team as a code owner June 17, 2026 21:03

@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.

I think this could all be much simpler (and more general) if options were a vector of std::pair<HeapType, Exactness>. Then the loop where we populate options could take exactness into account and we wouldn't have to deal with it afterward.

@kripken

kripken commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

I tried that a little but I don't think it avoids complexity? We still need to handle the case of the old type beginning exact, and only allowing exact for the exact heap type in the new type, make sure not to make a basic type exact - i.e. all the same stuff?

@tlively

tlively commented Jun 17, 2026

Copy link
Copy Markdown
Member

Here's what I have in mind:

diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index 2e1e9281b..00075e05b 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -2487,32 +2487,41 @@ void TranslateToFuzzReader::mutateJSBoundary() {
     if (new_ == Type::unreachable) {
       new_ = Type(old.getHeapType().getBottom(), NonNullable);
     }
+    assert(Type::isSubType(new_, old));
 
     // Find all heap types between the old and new, starting from new.
     auto oldHeapType = old.getHeapType();
+    auto oldExactness = old.getExactness();
     auto newHeapType = new_.getHeapType();
-    assert(HeapType::isSubType(newHeapType, oldHeapType));
-    std::vector<HeapType> options;
+    auto newExactness = new_.getExactness();
+    std::vector<std::pair<HeapType, Exactness>> options;
     while (1) {
-      options.push_back(newHeapType);
+      options.push_back({newHeapType, newExactness});
       // We cannot look at a bottom type's supers (there can be many, and the
       // getSuperType() API doesn't return them), but can use
       // interestingHeapSubTypes: any subtype of old is valid.
       if (newHeapType.isBottom()) {
         for (auto type : interestingHeapSubTypes[oldHeapType]) {
-          options.push_back(type);
+          if (type != oldHeapType || oldExactness == Inexact) {
+            options.push_back({type, Inexact});
+          }
+          options.push_back({type, Exact});
         }
         break;
       }
       // Continue until we reach the old type.
-      if (newHeapType == oldHeapType) {
+      if (newHeapType == oldHeapType && newExactness == oldExactness) {
         break;
       }
+      if (newExactness == Exact) {
+        newExactness = Inexact;
+        continue;
+      }
       auto next = newHeapType.getSuperType();
       assert(next);
       newHeapType = *next;
     }
-    newHeapType = pick(options);
+    auto [heapType, exactness] = pick(options);
 
     // Pick the nullability.
     auto oldNullability = old.getNullability();
@@ -2521,23 +2530,7 @@ void TranslateToFuzzReader::mutateJSBoundary() {
       newNullability = getNullability();
     }
 
-    // Pick the exactness.
-    auto oldExactness = old.getExactness();
-    auto newExactness = new_.getExactness();
-    // We can only be exact if we are using the new heap type: that type is
-    // exactly what is sent here, and no intermediate heap type would be valid.
-    // For example, given $A :> $B :> $C, then maybeRefine($A, exact $C) can
-    // return exact $C, but cannot return exact $B.
-    //
-    // Also, basic heap types cannot be exact.
-    if (newHeapType != new_.getHeapType() || newHeapType.isBasic()) {
-      newExactness = Inexact;
-    } else if (newExactness != oldExactness) {
-      // TODO: once getExactness() is fixed (see there), use that
-      newExactness = oneIn(2) ? Exact : Inexact;
-    }
-
-    return Type(newHeapType, newNullability, newExactness);
+    return Type(heapType, newNullability, exactness);
   };
 
   // Given a set of types (all params or all results), and an index among them,

This is a net reduction in code.

@kripken

kripken commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

That is shorter, but iianm, given old type exact $A and new type nullref, it has a chance to emit exact $A' (sibling type), which is not a subtype.

@tlively

tlively commented Jun 18, 2026

Copy link
Copy Markdown
Member

Sure, I guess I misplaced the new if in the newHeapType.isBottom() case. The loop over interestingHeapSubTypes[oldHeapType] can be done only when the old type is inexact.

@kripken

kripken commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Ok, there was one more bug - in the exact case, that code didn't add the old exact type - but after fixing that, I think it is still simpler to do it in the loop as you suggested, and I checked and all other cases seem to be handled. Updated.

@kripken

kripken commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Actually there was yet one more bug, see last commit.

Comment thread src/tools/fuzzing/fuzzing.cpp Outdated
Comment on lines +2514 to +2516
// Regardless of the old exactness, it is valid to add the old type as
// exact.
options.push_back({oldHeapType, 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.

What about the case where new_ == old and they are inexact?

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.

Good point - yet another if is needed here 😄

Fixed.

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 don't think the fix addresses the problem. We could have new_ == old where they are inexact and not basic and it would still be invalid to add the exact version of the old type.

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.

But all this is inside if (newHeapType.isBottom()) {

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.

Ah, of course.

@kripken kripken merged commit d931043 into WebAssembly:main Jun 18, 2026
16 checks passed
@kripken kripken deleted the fuzz.exact branch June 18, 2026 20:35
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