-
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
Conversation
The logic for joining nullability into a PossibleContents cone value did not previously preserve the exactness of the type in the cone value, causing assertion failures.
Exact references are known to point to exactly their heap type and not one of its subtypes. GUFA already analyzed exactness via the more general "cone" types that include an allowed subtyping depth. Exact types are equivalent to cone types of depth zero. Let GUFA take advantage of exactness information by normalizing cone depths to 0 whenever the cone type is exact.
Array2Struct has to update the types of every expression that interacts with and produces a reference to the optimized array type. It previously did this by separately checking whether a nullable or non-nullable reference to the array was a subtype of the expression's type. Simplify this logic by doing only a single check that considers only the heap types of the references. Also remove some unnecessary variables in which various reference types were cached since it is extremely cheap to materialize a reference type now. These simplifications will also make it easier to update the pass to handle exact reference types once `array.new` instructions are typed as exact.
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.
src/ir/gc-type-utils.h
Outdated
@@ -104,7 +104,19 @@ 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 be compatible with type `b` |
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.
what does "compatible" here mean, if not "is a subtype" - ?
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.
I will simplify this to "Check whether a value of type a
is known to have type b
". The only reason not to use subtype
is because we're relating a value and a type, not two types.
} | ||
// Ignore nullability. | ||
return Type::isSubType(a.with(NonNullable), b.with(NonNullable)); | ||
}; |
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.
I'm still not following this comment+code. If a
is a user-defined heap type, why does it matter if it is open or not?
E.g. I would expect isHeapSubtype(child, parent)
to return true regardless of the openness of child
, since child
"also has type" parent
. Or do I not understand what "also has type" means?
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.
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 comment
The 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 comment
The 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 comment
The 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.
// 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) { | ||
// TODO: Use information from a subtypes analysis, if available. |
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.
// TODO: Use information from a subtypes analysis, if available. | |
// Infer if a must be exact: if it has no subtypes, then it must be. | |
// TODO: Use information from a subtypes analysis, if available. |
} | ||
// Ignore nullability. | ||
return Type::isSubType(a.with(NonNullable), b.with(NonNullable)); | ||
}; |
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.
Thanks, now I can follow the code here.
I wrote a comment suggestion above.
src/passes/Heap2Local.cpp
Outdated
@@ -1248,7 +1242,7 @@ struct Array2Struct : PostWalker<Array2Struct> { | |||
// type here unconditionally, since we know the allocation flows through | |||
// here, and anyhow we will be removing the reference during Struct2Local, | |||
// later.) | |||
curr->type = nonNullStruct; | |||
curr->type = Type(structType, NonNullable); |
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.
Was this diff not part of another PR?
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.
Yes. That PR was merged, so now it appears here :(
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 variousinteresting heap type relationships.