Skip to content

Commit 5e78055

Browse files
committed
Extract per-declaration type param alignment into entry-level align_params
The substitution that renames a declaration's type parameters to the entry's type parameters was built inline in five places: MethodBuilder, DefinitionBuilder#define_instance, AncestorBuilder#mixin_ancestors, ModuleEntry#self_types, and AncestorBuilder#validate_super_class!. Define it once as ModuleEntry#align_params / ClassEntry#align_params and use it from all of them. The method returns nil when the declaration already uses the entry's type parameter names, so the callers can skip the substitution in the common case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TBY8ct4HpkVkZNPdsNHEDE
1 parent 1e2caae commit 5e78055

8 files changed

Lines changed: 73 additions & 30 deletions

File tree

lib/rbs/definition_builder.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,13 @@ def define_instance(definition, type_name, subst, define_class_vars:)
124124
end
125125

126126
entry = env.class_decls[type_name] or raise "Unknown name for build_instance: #{type_name}"
127-
args = entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
128127

129128
entry.each_decl do |decl|
130-
subst_ = subst + Substitution.build(decl.type_params.each.map(&:name), args)
129+
if align_params = entry.align_params(decl)
130+
subst_ = subst + align_params
131+
else
132+
subst_ = subst
133+
end
131134

132135
decl.members.each do |member|
133136
case member

lib/rbs/definition_builder/ancestor_builder.rb

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,11 @@ def validate_super_class!(type_name, entry)
177177

178178
return if with_super_classes.size <= 1
179179

180-
entry_param_names = entry.type_params.map(&:name)
181-
182180
super_types = with_super_classes.map do |decl|
183181
super_class = decl.super_class or raise
184182
args = super_class.args
185183

186-
decl_param_names = decl.type_params.map(&:name)
187-
unless decl_param_names == entry_param_names || args.empty?
188-
align_params = Substitution.build(
189-
decl_param_names,
190-
entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
191-
)
184+
if align_params = entry.align_params(decl)
192185
args = args.map {|type| type.sub(align_params) }
193186
end
194187

@@ -486,10 +479,7 @@ def mixin_ancestors0(decl, type_name, align_params:, included_modules:, included
486479

487480
def mixin_ancestors(entry, type_name, included_modules:, included_interfaces:, extended_modules:, prepended_modules:, extended_interfaces:)
488481
entry.each_decl do |decl|
489-
align_params = Substitution.build(
490-
decl.type_params.each.map(&:name),
491-
entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
492-
)
482+
align_params = entry.align_params(decl)
493483

494484
mixin_ancestors0(decl,
495485
type_name,

lib/rbs/definition_builder/method_builder.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def build_instance(type_name)
104104
type = Types::ClassInstance.new(name: type_name, args: args, location: nil)
105105
Methods.new(type: type).tap do |methods|
106106
entry.each_decl do |decl|
107-
subst = Substitution.build(decl.type_params.each.map(&:name), args)
107+
subst = entry.align_params(decl)
108108
case decl
109109
when AST::Declarations::Base
110110
each_rbs_member_with_accessibility(decl.members) do |member, accessibility|
@@ -115,22 +115,22 @@ def build_instance(type_name)
115115
build_method(
116116
methods,
117117
type,
118-
member: member.update(overloads: member.overloads.map {|overload| overload.sub(subst) }),
118+
member: subst ? member.update(overloads: member.overloads.map {|overload| overload.sub(subst) }) : member,
119119
accessibility: member.visibility || accessibility
120120
)
121121
when :singleton_instance
122122
build_method(
123123
methods,
124124
type,
125-
member: member.update(overloads: member.overloads.map {|overload| overload.sub(subst) }),
125+
member: subst ? member.update(overloads: member.overloads.map {|overload| overload.sub(subst) }) : member,
126126
accessibility: :private
127127
)
128128
end
129129
when AST::Members::AttrReader, AST::Members::AttrWriter, AST::Members::AttrAccessor
130130
if member.kind == :instance
131131
build_attribute(methods,
132132
type,
133-
member: member.update(type: member.type.sub(subst)),
133+
member: subst ? member.update(type: member.type.sub(subst)) : member,
134134
accessibility: member.visibility || accessibility)
135135
end
136136
when AST::Members::Alias

lib/rbs/environment/class_entry.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ def validate_type_params
6464
end
6565
end
6666
end
67+
68+
def align_params(decl)
69+
entry_params = type_params
70+
decl_param_names = decl.type_params.map(&:name)
71+
72+
return nil if decl_param_names == entry_params.map(&:name)
73+
74+
Substitution.build(
75+
decl_param_names,
76+
entry_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
77+
)
78+
end
6779
end
6880
end
6981
end

lib/rbs/environment/module_entry.rb

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,13 @@ def type_params
4141
end
4242

4343
def self_types
44-
params = type_params
45-
param_names = params.map(&:name)
46-
4744
each_decl.flat_map do |decl|
4845
self_types = decl.self_types
49-
decl_param_names = decl.type_params.map(&:name)
46+
subst = align_params(decl)
5047

51-
if self_types.empty? || decl_param_names == param_names
48+
if self_types.empty? || subst.nil?
5249
self_types
5350
else
54-
# The declaration uses different type parameter names from the primary declaration.
55-
# Rename the type variables in the self types, so that they are aligned to `#type_params`.
56-
subst = Substitution.build(
57-
decl_param_names,
58-
params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
59-
)
60-
6151
self_types.map do |self_type|
6252
AST::Declarations::Module::Self.new(
6353
name: self_type.name,
@@ -69,6 +59,18 @@ def self_types
6959
end.uniq
7060
end
7161

62+
def align_params(decl)
63+
entry_params = type_params
64+
decl_param_names = decl.type_params.map(&:name)
65+
66+
return nil if decl_param_names == entry_params.map(&:name)
67+
68+
Substitution.build(
69+
decl_param_names,
70+
entry_params.map {|param| Types::Variable.new(name: param.name, location: param.location) }
71+
)
72+
end
73+
7274
def validate_type_params
7375
unless context_decls.empty?
7476
first_decl, *rest_decls = each_decl.to_a

sig/environment/class_entry.rbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ module RBS
4545
# * Raises `GenericParameterMismatchError` if incompatible declaration is detected.
4646
#
4747
def validate_type_params: () -> void
48+
49+
# Returns a substitution that renames the type parameters of the declaration to the entry's type parameters (`#type_params`)
50+
#
51+
# Returns `nil` if the declaration uses the same type parameter names as `#type_params`.
52+
#
53+
def align_params: (declaration | ModuleEntry::declaration) -> Substitution?
4854
end
4955
end
5056
end

sig/environment/module_entry.rbs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ module RBS
5454
# declarations, but `#location` points to the original declaration.
5555
#
5656
def self_types: () -> Array[AST::Declarations::Module::Self]
57+
58+
# Returns a substitution that renames the type parameters of the declaration to the entry's type parameters (`#type_params`)
59+
#
60+
# Returns `nil` if the declaration uses the same type parameter names as `#type_params`.
61+
#
62+
def align_params: (declaration | ClassEntry::declaration) -> Substitution?
5763
end
5864
end
5965
end

test/rbs/environment_test.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,30 @@ module Foo[C] : _Animal[Integer]
361361
end
362362
end
363363

364+
def test_module_entry_align_params
365+
_, _, decls = RBS::Parser.parse_signature(<<EOF)
366+
module Foo[A, B]
367+
end
368+
369+
module Foo[X, Y]
370+
end
371+
EOF
372+
373+
Environment::ModuleEntry.new(type_name("::Foo")).tap do |entry|
374+
entry << [nil, decls[0]]
375+
entry << [nil, decls[1]]
376+
377+
# Aligned to the primary declaration's names, so no substitution is needed
378+
assert_nil entry.align_params(decls[0])
379+
380+
entry.align_params(decls[1]).tap do |subst|
381+
subst or raise
382+
assert_equal RBS::Types::Variable.new(name: :A, location: nil), subst[RBS::Types::Variable.new(name: :X, location: nil)]
383+
assert_equal RBS::Types::Variable.new(name: :B, location: nil), subst[RBS::Types::Variable.new(name: :Y, location: nil)]
384+
end
385+
end
386+
end
387+
364388
def test_absolute_type
365389
env = Environment.new
366390

0 commit comments

Comments
 (0)