Skip to content

Commit 3aa32ea

Browse files
committed
Support default types, don't add types to overridden methods
1 parent 75cc62b commit 3aa32ea

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

spec/compiler/apply_types_spec.cr

+34-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ def run_source_typer_spec(input, expected_output,
1515

1616
typer.files.to_a.should eq [entrypoint_file]
1717
result = typer.type_source(entrypoint_file, input)
18-
result.should_not be_nil
19-
not_nil_result = result.not_nil!("Why is this failing???")
20-
not_nil_result.strip.should eq expected_output
18+
result.try(&.strip).should eq expected_output.try &.strip
2119
end
2220

2321
describe Crystal::SourceTyper do
@@ -412,6 +410,39 @@ describe Crystal::SourceTyper do
412410
OUTPUT
413411
end
414412

413+
it "types args and include default type" do
414+
run_source_typer_spec(<<-INPUT, <<-OUTPUT)
415+
def test(arg = nil)
416+
nil
417+
end
418+
test(3)
419+
INPUT
420+
def test(arg : Int32? = nil) : Nil
421+
nil
422+
end
423+
424+
test(3)
425+
OUTPUT
426+
end
427+
428+
it "doesn't type methods that are inherited" do
429+
run_source_typer_spec(<<-INPUT, nil, line_number: -1)
430+
class Foo
431+
def test(arg)
432+
nil
433+
end
434+
end
435+
436+
class Bar < Foo
437+
def test(arg)
438+
1
439+
end
440+
end
441+
442+
Bar.new.test(3)
443+
INPUT
444+
end
445+
415446
it "runs prelude and types everything" do
416447
run_source_typer_spec(<<-INPUT, <<-OUTPUT, line_number: -1, prelude: "prelude")
417448
# This file tries to capture each type of definition format

src/compiler/crystal/tools/typer.cr

+39-3
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,11 @@ module Crystal
144144

145145
program.types.each { |_, t| types << t }
146146

147+
overridden_method_locations = [] of String
147148
while type = types.shift?
148149
type.types?.try &.each { |_, t| types << t }
150+
# pp! type, def_overrides_parent_def(type)
151+
def_overrides_parent_def(type).each { |loc| overridden_method_locations << loc }
149152

150153
# Check for class instance 'def's
151154
if type.responds_to?(:def_instances)
@@ -167,9 +170,38 @@ module Crystal
167170
end
168171
end
169172

173+
# Now remove all overridden methods
174+
overridden_method_locations.each do |loc|
175+
ret.delete(loc)
176+
end
177+
170178
ret
171179
end
172180

181+
private def def_overrides_parent_def(type) : Array(String)
182+
overriden_locations = [] of String
183+
type.defs.try &.each_value do |defs_with_metadata|
184+
defs_with_metadata.each do |def_with_metadata|
185+
next if def_with_metadata.def.location.to_s.starts_with?("expanded macro:")
186+
type.ancestors.each do |ancestor|
187+
other_defs_with_metadata = ancestor.defs.try &.[def_with_metadata.def.name]?
188+
other_defs_with_metadata.try &.each do |other_def_with_metadata|
189+
next if other_def_with_metadata.def.location.to_s.starts_with?("expanded macro:")
190+
found_def_with_same_name = true
191+
192+
if def_with_metadata.compare_strictness(other_def_with_metadata, self_owner: type, other_owner: ancestor) == 0
193+
# puts "Method #{type}##{def_with_metadata.def.name} overrides #{ancestor}##{def_with_metadata.def.name}"
194+
# Found a method with the same name and same, stricter or weaker restriction,
195+
# so it overrides
196+
overriden_locations << def_with_metadata.def.location.to_s
197+
end
198+
end
199+
end
200+
end
201+
end
202+
overriden_locations
203+
end
204+
173205
# Given an 'arg', return its type that's good for printing (VirtualTypes suffix themselves with a '+')
174206
private def resolve_type(arg)
175207
t = arg.type
@@ -188,10 +220,7 @@ module Crystal
188220

189221
# Generates a map of (parsed) Def#location => Signature for that Def
190222
private def init_signatures(accepted_defs : Hash(String, Crystal::Def)) : Hash(String, Signature)
191-
# This is hard to read, but transforms the def_instances array into a hash of def.location -> its full Signature
192223
@_signatures ||= accepted_def_instances(accepted_defs).compact_map do |location, def_instances|
193-
# Finally, combine all def_instances for a single def_obj_id into a single signature
194-
195224
parsed = accepted_defs[location]
196225

197226
all_typed_args = Hash(String, Set(Crystal::Type)).new { |h, k| h[k] = Set(Crystal::Type).new }
@@ -235,6 +264,13 @@ module Crystal
235264
else
236265
raise "Unknown handling of arg #{arg} at #{def_instance.location} in #{def_instance}\n#{parsed}"
237266
end
267+
268+
# Special case - we can have default args that are never used be a different type than what was set.
269+
# Ensure those default arg types also get respected (i.e. `arg = nil` => `arg : Int32? = nil` instead
270+
# of `arg : Int32 = nil`)
271+
if def_val = arg.default_value
272+
all_typed_args[arg.external_name] << program.semantic(def_val).type
273+
end
238274
end
239275

240276
encountered_non_splat_arg_def_instance |= !encountered_splat_arg

0 commit comments

Comments
 (0)