Align module-self type params across declarations in ModuleEntry#self_types - #3067
Open
soutaro wants to merge 2 commits into
Open
Align module-self type params across declarations in ModuleEntry#self_types#3067soutaro wants to merge 2 commits into
soutaro wants to merge 2 commits into
Conversation
…_types
When a module has multiple declarations with different (but compatible)
type parameter names, `Environment::ModuleEntry#self_types` collected the
self type constraints of each declaration as-is. The type variables from
non-primary declarations were left as free variables that are not bound
to any type parameter of the module, so downstream tools (e.g. Steep's
module self type check) could never satisfy the constraint:
# a.rbs
module M[out A] : _Foo[A]
end
# b.rbs
module M[out B] : _Foo[B]
end
entry.self_types # => [_Foo[A], _Foo[B]] (B is unbound)
Mixin members already get this alignment via `align_params` in
`DefinitionBuilder::AncestorBuilder#mixin_ancestors`, but module self
types did not. Fix it in `ModuleEntry#self_types` — the aggregation
point every consumer goes through — by renaming the type variables of
each declaration's self types to the primary declaration's type
parameters, using the same substitution as `mixin_ancestors`. The
`location` of substituted self types keeps pointing to the original
declaration, so error locations (NoSelfTypeFoundError,
InvalidTypeApplicationError) are unchanged.
Also drop `location` from `AST::Declarations::Module::Self#hash` to make
it consistent with `#==`/`#eql?`, which only compare `name` and `args`.
The inconsistency made the `.uniq` in `ModuleEntry#self_types`
ineffective across files, so identical self types from different
declarations were duplicated.
With both fixes, the example above now yields `[_Foo[A]]`.
This is what broke Steep's self check with rbs 4.1.2, where
core/enumerable.rbs renamed `Elem` to `E` while other environments still
declare `module Enumerable[unchecked out Elem] : _Each[Elem]`:
`one_instance_ancestors(::Enumerable).self_types` became
`[_Each[E, void], _Each[Elem, void]]`, failing every class that includes
Enumerable. (soutaro/steep#2256)
Note: `sig/shims/enumerable.rbs` intentionally keeps the `Elem` name —
this repository's own `steep check` runs on rbs 3.9 whose core still
uses `Elem`, and renaming the shim to `E` makes the self check fail
there. With the alignment fix the name difference is harmless.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBY8ct4HpkVkZNPdsNHEDE
The superclass comparison across multiple declarations compared the superclass args as written, so declarations that declare the same superclass with different type parameter names (`class C[A] < Base[A]` and `class C[B] < Base[B]`) raised a false SuperclassMismatchError. Align the args to the entry's type parameter names before comparing, like ModuleEntry#self_types and mixin_ancestors do. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TBY8ct4HpkVkZNPdsNHEDE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Environment::ModuleEntry#self_typescollected each declaration's self types as-is, so when declarations use different type parameter names (module M[A] : _Foo[A]in one file,module M[B] : _Foo[B]in another), the result contained free variables not bound to the module's type parameters —[_Foo[A], _Foo[B]]instead of[_Foo[A]]. This broke Steep's module self type check for every class includingEnumerable, because rbs 4.1.2 renamedcore/enumerable.rbs'sElemtoEwhilesig/shims/enumerable.rbsstill usesElem(soutaro/steep#2256).This PR applies the same per-declaration substitution as
AncestorBuilder#mixin_ancestorsinModuleEntry#self_types, renaming the variables to the primary declaration's type parameter names. The rebuiltModule::Selfkeeps the originallocation, so error positions and ancestor sources are unchanged. Also,Module::Self#hashno longer includeslocation.hash—==/eql?only comparenameandargs, and the mismatch prevented.uniqfrom deduplicating equal self types across files.AncestorBuilder#validate_super_class!had the same problem for superclasses:class C[A] < Base[A]andclass C[B] < Base[B]raised a falseSuperclassMismatchError. It now aligns the superclass args to the entry's type parameter names before comparing.🤖 Generated with Claude Code
https://claude.ai/code/session_01TBY8ct4HpkVkZNPdsNHEDE