From da3529591c846bb5811c16913bb89403ae6900ae Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 17 Jun 2026 13:57:40 -0700 Subject: [PATCH 1/5] fix --- src/tools/fuzzing/fuzzing.cpp | 37 ++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 2e1e9281b07..5746351a300 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2524,20 +2524,35 @@ void TranslateToFuzzReader::mutateJSBoundary() { // 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()) { + if (newHeapType.isBasic()) { + // Basic heap types cannot be exact. newExactness = Inexact; - } else if (newExactness != oldExactness) { - // TODO: once getExactness() is fixed (see there), use that - newExactness = oneIn(2) ? Exact : Inexact; + } else { + // We are refining to a non-basic type. This 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. + if (newHeapType != new_.getHeapType()) { + newExactness = Inexact; + if (newHeapType != oldHeapType && oldExactness == Exact) { + // We are refining the heap type (i.e. it changes), but it was exact. + // We must be exact as well, to be a subtype, which means we must use + // the same heap type. + newHeapType = oldHeapType; + newExactness = Exact; + } + } else if (newExactness != oldExactness) { + // We are refining to the new heap type, and it is exact while before it + // was not, so both are possible. + // TODO: once getExactness() is fixed (see there), use that + newExactness = oneIn(2) ? Exact : Inexact; + } } - return Type(newHeapType, newNullability, newExactness); + auto refined = Type(newHeapType, newNullability, newExactness); + assert(Type::isSubType(refined, old)); + return refined; }; // Given a set of types (all params or all results), and an index among them, From b2fb4729cd69b7a99c23e34a92a8cbc09ff5ea90 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 18 Jun 2026 10:06:20 -0700 Subject: [PATCH 2/5] other.way --- src/tools/fuzzing/fuzzing.cpp | 64 ++++++++++++++--------------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 5746351a300..8f2fdbc9462 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2487,32 +2487,49 @@ 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 options; + auto newExactness = new_.getExactness(); + std::vector> 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); + // We can only do this when the old exactness is inexact: if old was + // (exact $A) then the only valid subtypes are (exact $A) itself, and + // the bottom type. + if (oldExactness == Inexact) { + for (auto type : interestingHeapSubTypes[oldHeapType]) { + options.push_back({type, Inexact}); + options.push_back({type, Exact}); + } } break; } - // Continue until we reach the old type. - if (newHeapType == oldHeapType) { + // Continue until we reach the old type and exactness. + if (newHeapType == oldHeapType && newExactness == oldExactness) { break; } + if (newExactness == Exact) { + // We are not at the old type and exactness yet (or we would have just + // stopped). Remove exactness, as the only exact result that is valid is + // newHeapType itself. That is, if the actual output is (exact $B) then + // we cannot return (exact $A) for some supertype $A, as that would + // break subtyping. + 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,36 +2538,7 @@ void TranslateToFuzzReader::mutateJSBoundary() { newNullability = getNullability(); } - // Pick the exactness. - auto oldExactness = old.getExactness(); - auto newExactness = new_.getExactness(); - if (newHeapType.isBasic()) { - // Basic heap types cannot be exact. - newExactness = Inexact; - } else { - // We are refining to a non-basic type. This 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. - if (newHeapType != new_.getHeapType()) { - newExactness = Inexact; - if (newHeapType != oldHeapType && oldExactness == Exact) { - // We are refining the heap type (i.e. it changes), but it was exact. - // We must be exact as well, to be a subtype, which means we must use - // the same heap type. - newHeapType = oldHeapType; - newExactness = Exact; - } - } else if (newExactness != oldExactness) { - // We are refining to the new heap type, and it is exact while before it - // was not, so both are possible. - // TODO: once getExactness() is fixed (see there), use that - newExactness = oneIn(2) ? Exact : Inexact; - } - } - - auto refined = Type(newHeapType, newNullability, newExactness); + auto refined = Type(heapType, newNullability, exactness); assert(Type::isSubType(refined, old)); return refined; }; From e9809c5602071efecd09f4865b0bbf94a82e4da1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 18 Jun 2026 10:08:40 -0700 Subject: [PATCH 3/5] fix --- src/tools/fuzzing/fuzzing.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 8f2fdbc9462..6f076ced7dd 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2509,6 +2509,10 @@ void TranslateToFuzzReader::mutateJSBoundary() { options.push_back({type, Inexact}); options.push_back({type, Exact}); } + } else { + // The old exactness is exact, so after adding the bottom, add exact + // of the old type, and stop. + options.push_back({oldHeapType, Exact}); } break; } From f8c96799a3c93b22bcb175aefb7790f27af83ff6 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 18 Jun 2026 10:12:01 -0700 Subject: [PATCH 4/5] fix another --- src/tools/fuzzing/fuzzing.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 6f076ced7dd..ea6765f6576 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2509,11 +2509,11 @@ void TranslateToFuzzReader::mutateJSBoundary() { options.push_back({type, Inexact}); options.push_back({type, Exact}); } - } else { - // The old exactness is exact, so after adding the bottom, add exact - // of the old type, and stop. - options.push_back({oldHeapType, Exact}); + options.push_back({oldHeapType, Inexact}); } + // Regardless of the old exactness, it is valid to add the old type as + // exact. + options.push_back({oldHeapType, Exact}); break; } // Continue until we reach the old type and exactness. From b0780be89095a611615de529122f954364e9d63f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 18 Jun 2026 10:39:43 -0700 Subject: [PATCH 5/5] feedback --- src/tools/fuzzing/fuzzing.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index ea6765f6576..63453d5e2ac 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2512,8 +2512,10 @@ void TranslateToFuzzReader::mutateJSBoundary() { options.push_back({oldHeapType, Inexact}); } // Regardless of the old exactness, it is valid to add the old type as - // exact. - options.push_back({oldHeapType, Exact}); + // exact (unless the old type was a basic type). + if (!oldHeapType.isBasic()) { + options.push_back({oldHeapType, Exact}); + } break; } // Continue until we reach the old type and exactness.