Skip to content

Commit e43be97

Browse files
authored
Update evaluateCastCheck for exact types (#7541)
Additionally use finality of heap types to infer that a value being cast must have exactly its static type, which allows us to infer that more casts to exact types will be successful. Add exhaustive unit tests for `evaluateCastCheck` for various interesting heap type relationships.
1 parent 637326b commit e43be97

File tree

3 files changed

+230
-3
lines changed

3 files changed

+230
-3
lines changed

src/ir/gc-type-utils.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,21 @@ inline EvaluationResult evaluateCastCheck(Type refType, Type castType) {
104104
}
105105

106106
auto castHeapType = castType.getHeapType();
107-
auto refIsHeapSubType = HeapType::isSubType(refHeapType, castHeapType);
107+
108+
// Check whether a value of type `a` is known to also have type `b`, assuming
109+
// it is non-null.
110+
auto isHeapSubtype = [](Type a, Type b) {
111+
// If the heap type of `a` has no subtypes, then we know its value must be
112+
// exactly `a`.
113+
// TODO: Use information from a subtypes analysis, if available.
114+
if (!a.getHeapType().isBasic() && !a.getHeapType().isOpen()) {
115+
a = a.with(Exact);
116+
}
117+
// Ignore nullability.
118+
return Type::isSubType(a.with(NonNullable), b.with(NonNullable));
119+
};
120+
121+
auto refIsHeapSubType = isHeapSubtype(refType, castType);
108122

109123
if (refIsHeapSubType) {
110124
// The heap type is a subtype. All we need is for nullability to work out as
@@ -121,7 +135,7 @@ inline EvaluationResult evaluateCastCheck(Type refType, Type castType) {
121135
return SuccessOnlyIfNonNull;
122136
}
123137

124-
auto castIsHeapSubType = HeapType::isSubType(castHeapType, refHeapType);
138+
auto castIsHeapSubType = isHeapSubtype(castType, refType);
125139
bool heapTypesCompatible = refIsHeapSubType || castIsHeapSubType;
126140

127141
if (!heapTypesCompatible || castHeapType.isBottom()) {

test/gtest/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ endif()
66

77
set(unittest_SOURCES
88
arena.cpp
9-
source-map.cpp
9+
cast-check.cpp
1010
cfg.cpp
1111
dfa_minimization.cpp
1212
disjoint_sets.cpp
@@ -18,6 +18,7 @@ set(unittest_SOURCES
1818
possible-contents.cpp
1919
printing.cpp
2020
scc.cpp
21+
source-map.cpp
2122
stringify.cpp
2223
suffix_tree.cpp
2324
topological-sort.cpp

test/gtest/cast-check.cpp

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Copyright 2025 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "ir/gc-type-utils.h"
18+
#include "type-test.h"
19+
#include "wasm-type.h"
20+
#include "gtest/gtest.h"
21+
22+
namespace wasm {
23+
24+
using namespace GCTypeUtils;
25+
26+
class CastCheckTest : public TypeTest {
27+
protected:
28+
HeapType super, sub, subFinal;
29+
30+
void SetUp() override {
31+
TypeBuilder builder(3);
32+
builder[0] = Struct();
33+
builder[0].setOpen();
34+
builder[1] = Struct();
35+
builder[1].subTypeOf(builder[0]).setOpen();
36+
builder[2] = Struct();
37+
builder[2].subTypeOf(builder[0]);
38+
39+
auto built = builder.build();
40+
ASSERT_TRUE(built);
41+
42+
super = (*built)[0];
43+
sub = (*built)[1];
44+
subFinal = (*built)[2];
45+
}
46+
};
47+
48+
TEST_F(CastCheckTest, CastToSelfNonFinal) {
49+
#define EXPECT_CAST( \
50+
srcNullability, srcExactness, castNullability, castExactness, result) \
51+
EXPECT_EQ(evaluateCastCheck(Type(super, srcNullability, srcExactness), \
52+
Type(super, castNullability, castExactness)), \
53+
result);
54+
55+
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Success);
56+
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, Unknown);
57+
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, SuccessOnlyIfNonNull);
58+
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Unknown);
59+
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, Success);
60+
EXPECT_CAST(Nullable, Exact, Nullable, Exact, Success);
61+
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, SuccessOnlyIfNonNull);
62+
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, SuccessOnlyIfNonNull);
63+
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Success);
64+
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Unknown);
65+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Success);
66+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Unknown);
67+
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Success);
68+
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Success);
69+
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Success);
70+
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Success);
71+
#undef EXPECT_CAST
72+
}
73+
74+
TEST_F(CastCheckTest, CastToSelfFinal) {
75+
#define EXPECT_CAST( \
76+
srcNullability, srcExactness, castNullability, castExactness, result) \
77+
EXPECT_EQ(evaluateCastCheck(Type(subFinal, srcNullability, srcExactness), \
78+
Type(subFinal, castNullability, castExactness)), \
79+
result);
80+
81+
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Success);
82+
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, Success);
83+
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, SuccessOnlyIfNonNull);
84+
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, SuccessOnlyIfNonNull);
85+
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, Success);
86+
EXPECT_CAST(Nullable, Exact, Nullable, Exact, Success);
87+
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, SuccessOnlyIfNonNull);
88+
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, SuccessOnlyIfNonNull);
89+
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Success);
90+
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Success);
91+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Success);
92+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Success);
93+
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Success);
94+
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Success);
95+
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Success);
96+
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Success);
97+
#undef EXPECT_CAST
98+
}
99+
100+
TEST_F(CastCheckTest, CastToSuper) {
101+
#define EXPECT_CAST( \
102+
srcNullability, srcExactness, castNullability, castExactness, result) \
103+
EXPECT_EQ(evaluateCastCheck(Type(sub, srcNullability, srcExactness), \
104+
Type(super, castNullability, castExactness)), \
105+
result);
106+
107+
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Success);
108+
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, SuccessOnlyIfNull);
109+
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, SuccessOnlyIfNonNull);
110+
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Failure);
111+
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, Success);
112+
EXPECT_CAST(Nullable, Exact, Nullable, Exact, SuccessOnlyIfNull);
113+
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, SuccessOnlyIfNonNull);
114+
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, Failure);
115+
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Success);
116+
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Failure);
117+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Success);
118+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Failure);
119+
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Success);
120+
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Failure);
121+
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Success);
122+
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Failure);
123+
#undef EXPECT_CAST
124+
}
125+
126+
TEST_F(CastCheckTest, CastToSub) {
127+
#define EXPECT_CAST( \
128+
srcNullability, srcExactness, castNullability, castExactness, result) \
129+
EXPECT_EQ(evaluateCastCheck(Type(super, srcNullability, srcExactness), \
130+
Type(sub, castNullability, castExactness)), \
131+
result);
132+
133+
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Unknown);
134+
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, Unknown);
135+
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, Unknown);
136+
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Unknown);
137+
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, SuccessOnlyIfNull);
138+
EXPECT_CAST(Nullable, Exact, Nullable, Exact, SuccessOnlyIfNull);
139+
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, Failure);
140+
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, Failure);
141+
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Unknown);
142+
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Unknown);
143+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Unknown);
144+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Unknown);
145+
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Failure);
146+
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Failure);
147+
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Failure);
148+
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Failure);
149+
#undef EXPECT_CAST
150+
}
151+
152+
TEST_F(CastCheckTest, CastToSibling) {
153+
#define EXPECT_CAST( \
154+
srcNullability, srcExactness, castNullability, castExactness, result) \
155+
EXPECT_EQ(evaluateCastCheck(Type(sub, srcNullability, srcExactness), \
156+
Type(subFinal, castNullability, castExactness)), \
157+
result);
158+
159+
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, SuccessOnlyIfNull);
160+
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, SuccessOnlyIfNull);
161+
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, Failure);
162+
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Failure);
163+
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, SuccessOnlyIfNull);
164+
EXPECT_CAST(Nullable, Exact, Nullable, Exact, SuccessOnlyIfNull);
165+
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, Failure);
166+
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, Failure);
167+
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Failure);
168+
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Failure);
169+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Failure);
170+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Failure);
171+
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Failure);
172+
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Failure);
173+
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Failure);
174+
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Failure);
175+
#undef EXPECT_CAST
176+
}
177+
178+
TEST_F(CastCheckTest, CastToBottom) {
179+
#define EXPECT_CAST(srcNullability, srcExactness, castNullability, result) \
180+
EXPECT_EQ(evaluateCastCheck(Type(super, srcNullability, srcExactness), \
181+
Type(HeapType::none, castNullability, Inexact)), \
182+
result);
183+
184+
EXPECT_CAST(Nullable, Inexact, Nullable, SuccessOnlyIfNull);
185+
EXPECT_CAST(Nullable, Inexact, NonNullable, Failure);
186+
EXPECT_CAST(Nullable, Exact, Nullable, SuccessOnlyIfNull);
187+
EXPECT_CAST(Nullable, Exact, NonNullable, Failure);
188+
EXPECT_CAST(NonNullable, Inexact, Nullable, Failure);
189+
EXPECT_CAST(NonNullable, Inexact, NonNullable, Failure);
190+
EXPECT_CAST(NonNullable, Exact, Nullable, Failure);
191+
EXPECT_CAST(NonNullable, Exact, NonNullable, Failure);
192+
#undef EXPECT_CAST
193+
}
194+
195+
TEST_F(CastCheckTest, CastFromBottom) {
196+
#define EXPECT_CAST(srcNullability, castNullability, castExactness, result) \
197+
EXPECT_EQ(evaluateCastCheck(Type(HeapType::none, srcNullability, Inexact), \
198+
Type(super, castNullability, castExactness)), \
199+
result);
200+
201+
EXPECT_CAST(Nullable, Nullable, Inexact, Success);
202+
EXPECT_CAST(Nullable, Nullable, Exact, Success);
203+
EXPECT_CAST(Nullable, NonNullable, Inexact, Failure);
204+
EXPECT_CAST(Nullable, NonNullable, Exact, Failure);
205+
EXPECT_CAST(NonNullable, Nullable, Inexact, GCTypeUtils::Unreachable);
206+
EXPECT_CAST(NonNullable, Nullable, Exact, GCTypeUtils::Unreachable);
207+
EXPECT_CAST(NonNullable, NonNullable, Inexact, GCTypeUtils::Unreachable);
208+
EXPECT_CAST(NonNullable, NonNullable, Exact, GCTypeUtils::Unreachable);
209+
#undef EXPECT_CAST
210+
}
211+
212+
} // namespace wasm

0 commit comments

Comments
 (0)