-
Notifications
You must be signed in to change notification settings - Fork 779
Update evaluateCastCheck for exact types #7541
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
Changes from all commits
dda5f7b
a51e2da
a868b6f
44b9b60
0f68de3
dcf2ef1
ba2488a
229b3e7
0ffc664
5f9bdda
b97feb7
82feeea
eef2504
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,7 +104,21 @@ inline EvaluationResult evaluateCastCheck(Type refType, Type castType) { | |
} | ||
|
||
auto castHeapType = castType.getHeapType(); | ||
auto refIsHeapSubType = HeapType::isSubType(refHeapType, castHeapType); | ||
|
||
// Check whether a value of type `a` is known to also have type `b`, assuming | ||
// it is non-null. | ||
auto isHeapSubtype = [](Type a, Type b) { | ||
// If the heap type of `a` has no subtypes, then we know its value must be | ||
// exactly `a`. | ||
// TODO: Use information from a subtypes analysis, if available. | ||
if (!a.getHeapType().isBasic() && !a.getHeapType().isOpen()) { | ||
a = a.with(Exact); | ||
} | ||
// Ignore nullability. | ||
return Type::isSubType(a.with(NonNullable), b.with(NonNullable)); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still not following this comment+code. If E.g. I would expect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "heap type" being checked here includes exactness, which is why we are actually comparing types and selectively ignoring nullability. We're not just comparing the static type of the cast input with the cast target type. Instead, we're comparing the most specific safe approximation of the dynamic type of the cast input we can get with the cast target. If we know the value has dynamic heap type $foo, and $foo is final and cannot have subtypes, then we also know that the value must have dynamic heap type (exact $foo) because it cannot have any other type and still be a $foo. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, what is a "dynamic type"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type of the value at runtime. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, now I can follow the code here. I wrote a comment suggestion above. |
||
|
||
auto refIsHeapSubType = isHeapSubtype(refType, castType); | ||
|
||
if (refIsHeapSubType) { | ||
// 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) { | |
return SuccessOnlyIfNonNull; | ||
} | ||
|
||
auto castIsHeapSubType = HeapType::isSubType(castHeapType, refHeapType); | ||
auto castIsHeapSubType = isHeapSubtype(castType, refType); | ||
bool heapTypesCompatible = refIsHeapSubType || castIsHeapSubType; | ||
|
||
if (!heapTypesCompatible || castHeapType.isBottom()) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
/* | ||
* Copyright 2025 WebAssembly Community Group participants | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include "ir/gc-type-utils.h" | ||
#include "type-test.h" | ||
#include "wasm-type.h" | ||
#include "gtest/gtest.h" | ||
|
||
namespace wasm { | ||
|
||
using namespace GCTypeUtils; | ||
|
||
class CastCheckTest : public TypeTest { | ||
protected: | ||
HeapType super, sub, subFinal; | ||
|
||
void SetUp() override { | ||
TypeBuilder builder(3); | ||
builder[0] = Struct(); | ||
builder[0].setOpen(); | ||
builder[1] = Struct(); | ||
builder[1].subTypeOf(builder[0]).setOpen(); | ||
builder[2] = Struct(); | ||
builder[2].subTypeOf(builder[0]); | ||
|
||
auto built = builder.build(); | ||
ASSERT_TRUE(built); | ||
|
||
super = (*built)[0]; | ||
sub = (*built)[1]; | ||
subFinal = (*built)[2]; | ||
} | ||
}; | ||
|
||
TEST_F(CastCheckTest, CastToSelfNonFinal) { | ||
#define EXPECT_CAST( \ | ||
srcNullability, srcExactness, castNullability, castExactness, result) \ | ||
EXPECT_EQ(evaluateCastCheck(Type(super, srcNullability, srcExactness), \ | ||
Type(super, castNullability, castExactness)), \ | ||
result); | ||
|
||
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Success); | ||
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, Unknown); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Unknown); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, Success); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Exact, Success); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Unknown); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Unknown); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Success); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Success); | ||
#undef EXPECT_CAST | ||
} | ||
|
||
TEST_F(CastCheckTest, CastToSelfFinal) { | ||
#define EXPECT_CAST( \ | ||
srcNullability, srcExactness, castNullability, castExactness, result) \ | ||
EXPECT_EQ(evaluateCastCheck(Type(subFinal, srcNullability, srcExactness), \ | ||
Type(subFinal, castNullability, castExactness)), \ | ||
result); | ||
|
||
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Success); | ||
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, Success); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, Success); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Exact, Success); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Success); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Success); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Success); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Success); | ||
#undef EXPECT_CAST | ||
} | ||
|
||
TEST_F(CastCheckTest, CastToSuper) { | ||
#define EXPECT_CAST( \ | ||
srcNullability, srcExactness, castNullability, castExactness, result) \ | ||
EXPECT_EQ(evaluateCastCheck(Type(sub, srcNullability, srcExactness), \ | ||
Type(super, castNullability, castExactness)), \ | ||
result); | ||
|
||
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Success); | ||
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Failure); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, Success); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Exact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, SuccessOnlyIfNonNull); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Success); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Failure); | ||
#undef EXPECT_CAST | ||
} | ||
|
||
TEST_F(CastCheckTest, CastToSub) { | ||
#define EXPECT_CAST( \ | ||
srcNullability, srcExactness, castNullability, castExactness, result) \ | ||
EXPECT_EQ(evaluateCastCheck(Type(super, srcNullability, srcExactness), \ | ||
Type(sub, castNullability, castExactness)), \ | ||
result); | ||
|
||
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, Unknown); | ||
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, Unknown); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, Unknown); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Unknown); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Exact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, Failure); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Unknown); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Unknown); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Unknown); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Unknown); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Failure); | ||
#undef EXPECT_CAST | ||
} | ||
|
||
TEST_F(CastCheckTest, CastToSibling) { | ||
#define EXPECT_CAST( \ | ||
srcNullability, srcExactness, castNullability, castExactness, result) \ | ||
EXPECT_EQ(evaluateCastCheck(Type(sub, srcNullability, srcExactness), \ | ||
Type(subFinal, castNullability, castExactness)), \ | ||
result); | ||
|
||
EXPECT_CAST(Nullable, Inexact, Nullable, Inexact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Inexact, Nullable, Exact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Inexact, Failure); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Exact, Failure); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Inexact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Exact, Nullable, Exact, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Inexact, Failure); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Inexact, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Inexact, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Inexact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Inexact, Failure); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Exact, Failure); | ||
#undef EXPECT_CAST | ||
} | ||
|
||
TEST_F(CastCheckTest, CastToBottom) { | ||
#define EXPECT_CAST(srcNullability, srcExactness, castNullability, result) \ | ||
EXPECT_EQ(evaluateCastCheck(Type(super, srcNullability, srcExactness), \ | ||
Type(HeapType::none, castNullability, Inexact)), \ | ||
result); | ||
|
||
EXPECT_CAST(Nullable, Inexact, Nullable, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Inexact, NonNullable, Failure); | ||
EXPECT_CAST(Nullable, Exact, Nullable, SuccessOnlyIfNull); | ||
EXPECT_CAST(Nullable, Exact, NonNullable, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, Nullable, Failure); | ||
EXPECT_CAST(NonNullable, Inexact, NonNullable, Failure); | ||
EXPECT_CAST(NonNullable, Exact, Nullable, Failure); | ||
EXPECT_CAST(NonNullable, Exact, NonNullable, Failure); | ||
#undef EXPECT_CAST | ||
} | ||
|
||
TEST_F(CastCheckTest, CastFromBottom) { | ||
#define EXPECT_CAST(srcNullability, castNullability, castExactness, result) \ | ||
EXPECT_EQ(evaluateCastCheck(Type(HeapType::none, srcNullability, Inexact), \ | ||
Type(super, castNullability, castExactness)), \ | ||
result); | ||
|
||
EXPECT_CAST(Nullable, Nullable, Inexact, Success); | ||
EXPECT_CAST(Nullable, Nullable, Exact, Success); | ||
EXPECT_CAST(Nullable, NonNullable, Inexact, Failure); | ||
EXPECT_CAST(Nullable, NonNullable, Exact, Failure); | ||
EXPECT_CAST(NonNullable, Nullable, Inexact, GCTypeUtils::Unreachable); | ||
EXPECT_CAST(NonNullable, Nullable, Exact, GCTypeUtils::Unreachable); | ||
EXPECT_CAST(NonNullable, NonNullable, Inexact, GCTypeUtils::Unreachable); | ||
EXPECT_CAST(NonNullable, NonNullable, Exact, GCTypeUtils::Unreachable); | ||
#undef EXPECT_CAST | ||
} | ||
|
||
} // namespace wasm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.