Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
This PR implements the
occurs
check for type variables. This means, for a given type variable:redirect
s until we get a concrete type)This is the 1st step in the larger project of supporting recursive types in the type system, but only for nominal types – as outlined here. This check is not yet used anywhere, that will come in a follow-up PR.
Memory Management
During the occurs check, there are things we need to write down:
seen
so far (pushed & popped for each recursive branch)visited
and confirmed are not recursive (to avoid redundant work)To capture this, we use a
Scratch
struct that allocates some array lists that can be re-used across many occurs check. This is the same pattern thatunify
uses to hold intermediate data.Differences from the Rust Compiler
This implementation has not significant difference from the Rust compiler version. Originally, I did things slightly differently, but ultimately decided that it probably made the most sense to match the semantics of the Rust version. That said, if those reviewing like the original approach, we can easily revert.
Original "Differences from the Rust Compiler"
The Rust implementation of occurs takes a different approach to avoiding redundant work
Mark
field to track visited state during the checkThis requires:
Mark
values on each visited descriptorThe Zig version does not use
Mark
at all. Instead:This results in the same number of allocations, but with two key differences:
Mark
fields on descriptorsvisited
arrayseen
variables, though the total number ofseen
vars for any given recursive branch would be less than the total visited nodes.That said, I'm unsure what the average number of variables in a type is, but this approach assumes it would be relatively small. (We could also use a Map to check for seen/visited vars, which may perform better than an array)