Skip to content

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

Merged
merged 13 commits into from
Apr 23, 2025
Merged

Update evaluateCastCheck for exact types #7541

merged 13 commits into from
Apr 23, 2025

Conversation

tlively
Copy link
Member

@tlively tlively commented Apr 23, 2025

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.

tlively added 9 commits April 21, 2025 17:16
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.
@tlively tlively requested a review from kripken April 23, 2025 00:55
@@ -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`
Copy link
Member

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" - ?

Copy link
Member Author

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.

Base automatically changed from array2struct-simplify to main April 23, 2025 04:04
}
// Ignore nullability.
return Type::isSubType(a.with(NonNullable), b.with(NonNullable));
};
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member

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"?

Copy link
Member Author

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.

Copy link
Member

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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));
};
Copy link
Member

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.

@@ -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);
Copy link
Member

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?

Copy link
Member Author

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 :(

@tlively tlively merged commit e43be97 into main Apr 23, 2025
14 checks passed
@tlively tlively deleted the cast-check-tests branch April 23, 2025 19:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants