diff options
author | Alon Zakai <azakai@google.com> | 2022-09-28 11:30:56 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-28 18:30:56 +0000 |
commit | 7044da7fe6754c54bd7f4930eaa208e3081cabe3 (patch) | |
tree | d3b6169b8a56eb8e7eb2957b4f05f8c317468639 | |
parent | 7aa2cb9990e7dd3de1ef5831ef8fad348734aa70 (diff) | |
download | binaryen-7044da7fe6754c54bd7f4930eaa208e3081cabe3.tar.gz binaryen-7044da7fe6754c54bd7f4930eaa208e3081cabe3.tar.bz2 binaryen-7044da7fe6754c54bd7f4930eaa208e3081cabe3.zip |
[GUFA] Fix haveIntersection on comparing nullable with non-nullable (#5089)
We compared types and not heap types, so a difference in nullability
confused us. But at that point in the code, we've ruled out nulls, so we
should focus on heap types only.
-rw-r--r-- | src/ir/possible-contents.cpp | 25 | ||||
-rw-r--r-- | test/gtest/possible-contents.cpp | 7 | ||||
-rw-r--r-- | test/lit/passes/gufa-refs.wast | 11 |
3 files changed, 41 insertions, 2 deletions
diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 167890d63..836f7570d 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -148,6 +148,14 @@ bool PossibleContents::haveIntersection(const PossibleContents& a, auto aType = a.getType(); auto bType = b.getType(); + if (!aType.isRef() || !bType.isRef()) { + // At least one is not a reference. The only way they can intersect is if + // the type is identical. + return aType == bType; + } + + // From here on we focus on references. + if (aType.isNullable() && bType.isNullable()) { // Null is possible on both sides. Assume that an intersection can exist, // but we could be more precise here and check if the types belong to @@ -156,12 +164,25 @@ bool PossibleContents::haveIntersection(const PossibleContents& a, return true; } - if (a.hasExactType() && b.hasExactType() && a.getType() != b.getType()) { + // We ruled out a null on both sides, so at least one is non-nullable. If the + // other is a null then no chance for an intersection remains. + if (a.isNull() || b.isNull()) { + return false; + } + + // From here on we focus on references and can ignore the case of null - any + // intersection must be of a non-null value, so we can focus on the heap + // types. + auto aHeapType = aType.getHeapType(); + auto bHeapType = bType.getHeapType(); + + if (a.hasExactType() && b.hasExactType() && aHeapType != bHeapType) { // The values must be different since their types are different. return false; } - if (!Type::isSubType(aType, bType) && !Type::isSubType(bType, aType)) { + if (!HeapType::isSubType(aHeapType, bHeapType) && + !HeapType::isSubType(bHeapType, aHeapType)) { // No type can appear in both a and b, so the types differ, so the values // differ. return false; diff --git a/test/gtest/possible-contents.cpp b/test/gtest/possible-contents.cpp index 1e1f0e5c9..933e220bb 100644 --- a/test/gtest/possible-contents.cpp +++ b/test/gtest/possible-contents.cpp @@ -280,9 +280,16 @@ TEST_F(PossibleContentsTest, TestIntersection) { assertHaveIntersection(exactFuncSignatureType, exactFuncSignatureType); assertHaveIntersection(i32Zero, i32One); // TODO: this could be inferred false + // Exact types only differing by nullability can intersect (not on the null, + // but on something else). + assertHaveIntersection(exactAnyref, exactNonNullAnyref); + // Due to subtyping, an intersection might exist. assertHaveIntersection(funcGlobal, funcGlobal); assertHaveIntersection(funcGlobal, exactFuncSignatureType); + assertHaveIntersection(nonNullFuncGlobal, exactFuncSignatureType); + assertHaveIntersection(funcGlobal, exactNonNullFuncSignatureType); + assertHaveIntersection(nonNullFuncGlobal, exactNonNullFuncSignatureType); // Neither is a subtype of the other, but nulls are possible, so a null can be // the intersection. diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast index 24ebda381..42f1b349d 100644 --- a/test/lit/passes/gufa-refs.wast +++ b/test/lit/passes/gufa-refs.wast @@ -2766,6 +2766,9 @@ ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $ref.eq-zero (export "ref.eq-zero") ;; We do not track specific references, so only the types can be used here. @@ -2791,6 +2794,14 @@ ) ) ) + (drop + (ref.eq + (struct.new $struct + (i32.const 5) + ) + (ref.null $struct) + ) + ) ) ;; CHECK: (func $ref.eq-unknown (type $i32_ref?|$struct|_ref?|$struct|_ref?|$other|_ref|$struct|_ref|$struct|_ref|$other|_=>_none) (param $x i32) (param $struct (ref null $struct)) (param $struct2 (ref null $struct)) (param $other (ref null $other)) (param $nn-struct (ref $struct)) (param $nn-struct2 (ref $struct)) (param $nn-other (ref $other)) |