From 3a36bf5c3a5fa8dac1d67f46a942e3ef34e3fe98 Mon Sep 17 00:00:00 2001 From: Beta Ziliani Date: Fri, 16 Dec 2022 11:47:35 -0300 Subject: [PATCH] Fixing conflict (just removing the 'return' keyword) --- .../compiler/crystal/zero_one_or_many_spec.cr | 166 ------------------ src/compiler/crystal/codegen/call.cr | 4 +- src/compiler/crystal/interpreter/compiler.cr | 4 +- .../crystal/interpreter/multidispatch.cr | 6 +- src/compiler/crystal/semantic/bindings.cr | 47 ++--- src/compiler/crystal/semantic/call.cr | 73 ++++---- src/compiler/crystal/semantic/call_error.cr | 3 +- .../crystal/semantic/cleanup_transformer.cr | 64 +++---- src/compiler/crystal/semantic/cover.cr | 6 +- src/compiler/crystal/semantic/filters.cr | 2 +- .../crystal/semantic/fix_missing_types.cr | 2 +- src/compiler/crystal/semantic/lib.cr | 7 +- src/compiler/crystal/semantic/main_visitor.cr | 24 ++- src/compiler/crystal/semantic/match.cr | 19 +- .../crystal/semantic/method_lookup.cr | 16 +- src/compiler/crystal/semantic/type_merge.cr | 8 +- src/compiler/crystal/semantic/warnings.cr | 2 +- src/compiler/crystal/tools/context.cr | 12 +- src/compiler/crystal/tools/implementations.cr | 6 +- src/compiler/crystal/zero_one_or_many.cr | 117 ------------ 20 files changed, 165 insertions(+), 423 deletions(-) delete mode 100644 spec/compiler/crystal/zero_one_or_many_spec.cr delete mode 100644 src/compiler/crystal/zero_one_or_many.cr diff --git a/spec/compiler/crystal/zero_one_or_many_spec.cr b/spec/compiler/crystal/zero_one_or_many_spec.cr deleted file mode 100644 index 149a44539324..000000000000 --- a/spec/compiler/crystal/zero_one_or_many_spec.cr +++ /dev/null @@ -1,166 +0,0 @@ -require "spec" -require "../../../src/compiler/crystal/zero_one_or_many" - -describe Crystal::ZeroOneOrMany do - describe "initialize and size" do - it "creates without a value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.size.should eq(0) - ary.value.should be_nil - end - - it "creates with a value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.size.should eq(1) - ary.to_a.should eq([1]) - ary.value.should be_a(Int32) - end - end - - describe "as Indexable" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - expect_raises(IndexError) { ary[0] } - expect_raises(IndexError) { ary[1] } - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary[0].should eq(1) - expect_raises(IndexError) { ary[1] } - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary[0].should eq(1) - ary[1].should eq(2) - expect_raises(IndexError) { ary[2] } - end - end - - describe "#each" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.sum.should eq(0) - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.sum.should eq(1) - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary.sum.should eq(3) - end - end - - describe "#push element" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.push 1 - ary.to_a.should eq([1]) - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.push 2 - ary.to_a.should eq([1, 2]) - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.push 1 - ary << 2 - ary.push 3 - ary.to_a.should eq([1, 2, 3]) - end - end - - describe "#+ elements" do - it "when there's no value and elements is empty" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([] of Int32) - ary.empty?.should be_true - ary.value.should be_nil - end - - it "when there's no value and elements has a single value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1]) - ary.to_a.should eq([1]) - ary.value.should be_a(Int32) - end - - it "when there's no value and elements has more than one value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary.to_a.should eq([1, 2]) - ary.value.should be_a(Array(Int32)) - end - - it "when there's a single value" do - ary = Crystal::ZeroOneOrMany.new(1) - ary.concat([2, 3]) - ary.to_a.should eq([1, 2, 3]) - ary.value.should be_a(Array(Int32)) - end - - it "when there's two values" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2]) - ary.concat([3, 4]) - ary.to_a.should eq([1, 2, 3, 4]) - ary.value.should be_a(Array(Int32)) - end - end - - describe "#reject" do - it "when there's no value" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.reject! { true } - ary.empty?.should be_true - ary.value.should be_nil - end - - it "when there's a single value and it matches" do - ary = Crystal::ZeroOneOrMany(Int32).new(1) - ary.reject! { |x| x == 1 } - ary.empty?.should be_true - ary.value.should be_nil - end - - it "when there's a single value and it doesn't match" do - ary = Crystal::ZeroOneOrMany(Int32).new(1) - ary.reject! { |x| x == 2 } - ary.to_a.should eq([1]) - ary.value.should be_a(Int32) - end - - it "when there are three values and none matches" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2, 3]) - ary.reject! { |x| x == 4 } - ary.to_a.should eq([1, 2, 3]) - ary.value.should be_a(Array(Int32)) - end - - it "when there are three values and two match" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2, 3]) - ary.reject! { |x| x < 3 } - ary.to_a.should eq([3]) - ary.value.should be_a(Int32) - end - - it "when there are three values and all match" do - ary = Crystal::ZeroOneOrMany(Int32).new - ary.concat([1, 2, 3]) - ary.reject! { |x| x < 4 } - ary.empty?.should be_true - ary.value.should be_nil - end - end -end diff --git a/src/compiler/crystal/codegen/call.cr b/src/compiler/crystal/codegen/call.cr index 598204154ef0..2f92d217ff80 100644 --- a/src/compiler/crystal/codegen/call.cr +++ b/src/compiler/crystal/codegen/call.cr @@ -7,7 +7,7 @@ class Crystal::CodeGenVisitor end target_defs = node.target_defs - if target_defs.empty? + unless target_defs node.raise "BUG: no target defs" end @@ -394,7 +394,7 @@ class Crystal::CodeGenVisitor position_at_end current_def_label # Prepare this specific call - call.target_defs = ZeroOneOrMany.new(a_def) + call.target_defs = [a_def] of Def call.obj.try &.set_type(a_def.owner) call.args.zip(a_def.args) do |call_arg, a_def_arg| call_arg.set_type(a_def_arg.type) diff --git a/src/compiler/crystal/interpreter/compiler.cr b/src/compiler/crystal/interpreter/compiler.cr index bb6cadd88fc7..5f480e7d18ab 100644 --- a/src/compiler/crystal/interpreter/compiler.cr +++ b/src/compiler/crystal/interpreter/compiler.cr @@ -1802,7 +1802,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor end target_defs = node.target_defs - if target_defs.empty? + unless target_defs node.raise "BUG: no target defs" end @@ -2458,7 +2458,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor end target_defs = call.target_defs - if target_defs.empty? + unless target_defs call.raise "BUG: no target defs" end diff --git a/src/compiler/crystal/interpreter/multidispatch.cr b/src/compiler/crystal/interpreter/multidispatch.cr index 005c67dca47e..3c22a7dec8a5 100644 --- a/src/compiler/crystal/interpreter/multidispatch.cr +++ b/src/compiler/crystal/interpreter/multidispatch.cr @@ -39,7 +39,7 @@ require "../semantic/main_visitor" # end # ``` module Crystal::Repl::Multidispatch - def self.create_def(context : Context, node : Call, target_defs : ZeroOneOrMany(Def)) + def self.create_def(context : Context, node : Call, target_defs : Array(Def)) if node.block a_def = create_def_uncached(context, node, target_defs) @@ -70,7 +70,7 @@ module Crystal::Repl::Multidispatch a_def end - private def self.create_def_uncached(context : Context, node : Call, target_defs : ZeroOneOrMany(Def)) + private def self.create_def_uncached(context : Context, node : Call, target_defs : Array(Def)) autocast_types = nil # The generated multidispatch method should handle autocasted @@ -200,7 +200,7 @@ module Crystal::Repl::Multidispatch end call = Call.new(call_obj, node.name, call_args) - call.target_defs = ZeroOneOrMany(Def).new(target_def) + call.target_defs = [target_def] call.type = target_def.type if block diff --git a/src/compiler/crystal/semantic/bindings.cr b/src/compiler/crystal/semantic/bindings.cr index 1705b4688f63..bba14d9be92b 100644 --- a/src/compiler/crystal/semantic/bindings.cr +++ b/src/compiler/crystal/semantic/bindings.cr @@ -1,7 +1,7 @@ module Crystal class ASTNode - property dependencies = ZeroOneOrMany(ASTNode).new - property observers = ZeroOneOrMany(ASTNode).new + property! dependencies : Array(ASTNode) + property observers : Array(ASTNode)? property enclosing_call : Call? @dirty = false @@ -107,17 +107,17 @@ module Crystal end def bind_to(node : ASTNode) : Nil - bind(node) do - @dependencies.push node + bind(node) do |dependencies| + dependencies.push node node.add_observer self end end - def bind_to(nodes : Indexable(ASTNode)) : Nil + def bind_to(nodes : Indexable) : Nil return if nodes.empty? - bind do - @dependencies.concat nodes + bind do |dependencies| + dependencies.concat nodes nodes.each &.add_observer self end end @@ -130,7 +130,9 @@ module Crystal raise_frozen_type freeze_type, from_type, from end - yield + dependencies = @dependencies ||= [] of ASTNode + + yield dependencies new_type = type_from_dependencies new_type = map_type(new_type) if new_type @@ -151,28 +153,27 @@ module Crystal Type.merge dependencies end - def unbind_from(nodes : Nil) : Nil + def unbind_from(nodes : Nil) # Nothing to do end - def unbind_from(node : ASTNode) : Nil - @dependencies.reject! &.same?(node) + def unbind_from(node : ASTNode) + @dependencies.try &.reject! &.same?(node) node.remove_observer self end - def unbind_from(nodes : Enumerable(ASTNode)) : Nil - @dependencies.reject! { |dependency| - nodes.any? &.same?(dependency) - } + def unbind_from(nodes : Array(ASTNode)) + @dependencies.try &.reject! { |dep| nodes.any? &.same?(dep) } nodes.each &.remove_observer self end - def add_observer(observer : ASTNode) : Nil - @observers.push observer + def add_observer(observer) + observers = @observers ||= [] of ASTNode + observers.push observer end - def remove_observer(observer : ASTNode) : Nil - @observers.reject! &.same?(observer) + def remove_observer(observer) + @observers.try &.reject! &.same?(observer) end def set_enclosing_call(enclosing_call) @@ -194,9 +195,9 @@ module Crystal end def notify_observers - @observers.each &.update self + @observers.try &.each &.update self @enclosing_call.try &.recalculate - @observers.each &.propagate + @observers.try &.each &.propagate @enclosing_call.try &.propagate end @@ -268,8 +269,8 @@ module Crystal visited = Set(ASTNode).new.compare_by_identity owner_trace << node if node.type?.try &.includes_type?(owner) visited.add node - until node.dependencies.empty? - dependencies = node.dependencies.select { |dep| dep.type? && dep.type.includes_type?(owner) && !visited.includes?(dep) } + while deps = node.dependencies? + dependencies = deps.select { |dep| dep.type? && dep.type.includes_type?(owner) && !visited.includes?(dep) } if dependencies.size > 0 node = dependencies.first nil_reason = node.nil_reason if node.is_a?(MetaTypeVar) diff --git a/src/compiler/crystal/semantic/call.cr b/src/compiler/crystal/semantic/call.cr index a3c16f7c2a33..83fe5f7f88e0 100644 --- a/src/compiler/crystal/semantic/call.cr +++ b/src/compiler/crystal/semantic/call.cr @@ -7,7 +7,7 @@ class Crystal::Call property! scope : Type property with_scope : Type? property! parent_visitor : MainVisitor - property target_defs = ZeroOneOrMany(Def).new + property target_defs : Array(Def)? property expanded : ASTNode? property expanded_macro : Macro? property? uses_with_scope = false @@ -20,14 +20,15 @@ class Crystal::Call end def target_def - case target_defs.size - when 0 - ::raise "Zero target defs for #{self}" - when 1 - target_defs.first - else - ::raise "#{target_defs.size} target defs for #{self}" + if defs = @target_defs + if defs.size == 1 + return defs.first + else + ::raise "#{defs.size} target defs for #{self}" + end end + + ::raise "Zero target defs for #{self}" end def recalculate @@ -77,10 +78,10 @@ class Crystal::Call block = @block - unbind_from target_defs unless target_defs.empty? + unbind_from @target_defs if @target_defs unbind_from block.break if block - @target_defs = ZeroOneOrMany(Def).new + @target_defs = nil if block_arg = @block_arg replace_block_arg_with_block(block_arg) @@ -91,12 +92,11 @@ class Crystal::Call # If @target_defs is set here it means there was a recalculation # fired as a result of a recalculation. We keep the last one. - return unless target_defs.empty? + return if @target_defs - # TODO: optimize zero one or many @target_defs = matches - bind_to matches unless matches.empty? + bind_to matches if matches bind_to block.break if block if (parent_visitor = @parent_visitor) && matches @@ -109,13 +109,13 @@ class Crystal::Call end end - def lookup_matches : ZeroOneOrMany(Def) + def lookup_matches lookup_matches(with_autocast: false) rescue ex : RetryLookupWithLiterals lookup_matches(with_autocast: true) end - def lookup_matches(*, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches(*, with_autocast = false) if args.any? { |arg| arg.is_a?(Splat) || arg.is_a?(DoubleSplat) } lookup_matches_with_splat(with_autocast) else @@ -133,7 +133,7 @@ class Crystal::Call end end - def lookup_matches_with_splat(with_autocast) : ZeroOneOrMany(Def) + def lookup_matches_with_splat(with_autocast) # Check if all splat are of tuples arg_types = Array(Type).new(args.size * 2) named_args_types = nil @@ -184,7 +184,7 @@ class Crystal::Call lookup_matches_without_splat arg_types, named_args_types, with_autocast: with_autocast end - def lookup_matches_without_splat(arg_types, named_args_types, with_autocast) : ZeroOneOrMany(Def) + def lookup_matches_without_splat(arg_types, named_args_types, with_autocast) if obj = @obj lookup_matches_in(obj.type, arg_types, named_args_types, with_autocast: with_autocast) elsif name == "super" @@ -198,19 +198,15 @@ class Crystal::Call end end - def lookup_matches_in(owner : AliasType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : AliasType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) lookup_matches_in(owner.remove_alias, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) end - def lookup_matches_in(owner : UnionType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) - matches = ZeroOneOrMany(Def).new - owner.union_types.each { |type| - matches.concat lookup_matches_in(type, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) - } - matches + def lookup_matches_in(owner : UnionType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) + owner.union_types.flat_map { |type| lookup_matches_in(type, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) } end - def lookup_matches_in(owner : Program, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : Program, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents: search_in_parents, with_autocast: with_autocast) end @@ -218,26 +214,26 @@ class Crystal::Call lookup_matches_in program, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast end - def lookup_matches_in(owner : NonGenericModuleType | GenericModuleInstanceType | GenericType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : NonGenericModuleType | GenericModuleInstanceType | GenericType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) attach_subclass_observer owner including_types = owner.including_types if including_types lookup_matches_in(including_types, arg_types, named_args_types, search_in_parents: search_in_parents, with_autocast: with_autocast) else - ZeroOneOrMany(Def).new + [] of Def end end - def lookup_matches_in(owner : LibType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : LibType, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) raise "lib fun call is not supported in dispatch" end - def lookup_matches_in(owner : Type, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in(owner : Type, arg_types, named_args_types, self_type = nil, def_name = self.name, search_in_parents = true, with_autocast = false) lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents: search_in_parents, with_autocast: with_autocast) end - def lookup_matches_with_scope_in(owner, arg_types, named_args_types, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_with_scope_in(owner, arg_types, named_args_types, with_autocast = false) signature = CallSignature.new(name, arg_types, block, named_args_types) matches = lookup_matches_checking_expansion(owner, signature, with_autocast: with_autocast) @@ -255,7 +251,7 @@ class Crystal::Call instantiate signature, matches, owner, self_type: nil, with_autocast: with_autocast end - def lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents, search_in_toplevel = true, with_autocast = false) : ZeroOneOrMany(Def) + def lookup_matches_in_type(owner, arg_types, named_args_types, self_type, def_name, search_in_parents, search_in_toplevel = true, with_autocast = false) signature = CallSignature.new(def_name, arg_types, block, named_args_types) matches = check_tuple_indexer(owner, def_name, args, arg_types) @@ -357,13 +353,12 @@ class Crystal::Call end end - def instantiate(signature, matches, owner, self_type, with_autocast) : ZeroOneOrMany(Def) + def instantiate(signature, matches, owner, self_type, with_autocast) matches.each &.remove_literals if with_autocast block = @block - # TODO: use map - typed_defs = ZeroOneOrMany(Def).new + typed_defs = Array(Def).new(matches.size) matches.each do |match| check_visibility match @@ -458,7 +453,7 @@ class Crystal::Call typed_def.type = return_type if return_type.no_return? || return_type.nil_type? end - def check_tuple_indexer(owner, def_name, args, arg_types) : Matches? + def check_tuple_indexer(owner, def_name, args, arg_types) return unless args.size == 1 case def_name @@ -493,13 +488,13 @@ class Crystal::Call end end - def tuple_indexer_helper(args, arg_types, owner, instance_type, nilable) : Matches? + def tuple_indexer_helper(args, arg_types, owner, instance_type, nilable) index = tuple_indexer_helper_index(args.first, owner, instance_type, nilable) return unless index indexer_def = yield instance_type, index indexer_match = Match.new(indexer_def, arg_types, MatchContext.new(owner, owner)) - Matches.new(ZeroOneOrMany(Match).new(indexer_match), true) + Matches.new([indexer_match] of Match, true) end private def tuple_indexer_helper_index(arg, owner, instance_type, nilable) @@ -579,7 +574,7 @@ class Crystal::Call if index || nilable indexer_def = yield instance_type, (index || -1) indexer_match = Match.new(indexer_def, arg_types, MatchContext.new(owner, owner)) - Matches.new(ZeroOneOrMany(Match).new(indexer_match), true) + Matches.new([indexer_match] of Match, true) else raise "missing key '#{name}' for named tuple #{owner}" end @@ -710,7 +705,7 @@ class Crystal::Call signature = CallSignature.new(previous.name, arg_types, block, named_args_types) context = MatchContext.new(scope, scope, def_free_vars: previous.free_vars) match = Match.new(previous, arg_types, context, named_args_types) - matches = Matches.new(ZeroOneOrMany(Match).new(match), true) + matches = Matches.new([match] of Match, true) unless signature.match(previous_item, context) raise_matches_not_found scope, previous.name, arg_types, named_args_types, matches, with_autocast: with_autocast, number_autocast: !program.has_flag?("no_number_autocast") diff --git a/src/compiler/crystal/semantic/call_error.cr b/src/compiler/crystal/semantic/call_error.cr index 2984ed5fd4c2..875fd5269380 100644 --- a/src/compiler/crystal/semantic/call_error.cr +++ b/src/compiler/crystal/semantic/call_error.cr @@ -643,7 +643,8 @@ class Crystal::Call if obj.is_a?(InstanceVar) scope = self.scope ivar = scope.lookup_instance_var(obj.name) - if ivar.dependencies.size == 1 && ivar.dependencies.first.same?(program.nil_var) + deps = ivar.dependencies? + if deps && deps.size == 1 && deps.first.same?(program.nil_var) similar_name = scope.lookup_similar_instance_var_name(ivar.name) if similar_name msg << colorize(" (#{ivar.name} was never assigned a value, did you mean #{similar_name}?)").yellow.bold diff --git a/src/compiler/crystal/semantic/cleanup_transformer.cr b/src/compiler/crystal/semantic/cleanup_transformer.cr index dd3c4e03bc67..0d7aa41ced5b 100644 --- a/src/compiler/crystal/semantic/cleanup_transformer.cr +++ b/src/compiler/crystal/semantic/cleanup_transformer.cr @@ -562,42 +562,43 @@ module Crystal return exps end - target_defs = node.target_defs - if target_defs.size == 1 - if target_defs.first.is_a?(External) - check_args_are_not_closure node, "can't send closure to C function" - elsif obj_type && obj_type.extern? && node.name.ends_with?('=') - check_args_are_not_closure node, "can't set closure as C #{obj_type.type_desc} member" + if target_defs = node.target_defs + if target_defs.size == 1 + if target_defs[0].is_a?(External) + check_args_are_not_closure node, "can't send closure to C function" + elsif obj_type && obj_type.extern? && node.name.ends_with?('=') + check_args_are_not_closure node, "can't set closure as C #{obj_type.type_desc} member" + end end - end - current_def = @current_def + current_def = @current_def - target_defs.each do |target_def| - if @transformed.add?(target_def) - node.bubbling_exception do - @current_def = target_def - @def_nest_count += 1 - target_def.body = target_def.body.transform(self) - @def_nest_count -= 1 - @current_def = current_def + target_defs.each do |target_def| + if @transformed.add?(target_def) + node.bubbling_exception do + @current_def = target_def + @def_nest_count += 1 + target_def.body = target_def.body.transform(self) + @def_nest_count -= 1 + @current_def = current_def + end end - end - # If the current call targets a method that raises, the method - # where the call happens also raises. - current_def.raises = true if current_def && target_def.raises? - end + # If the current call targets a method that raises, the method + # where the call happens also raises. + current_def.raises = true if current_def && target_def.raises? + end - if target_defs.empty? - exps = [] of ASTNode - if obj = node.obj - exps.push obj + if node.target_defs.not_nil!.empty? + exps = [] of ASTNode + if obj = node.obj + exps.push obj + end + node.args.each { |arg| exps.push arg } + call_exps = Expressions.from exps + call_exps.set_type(exps.last.type?) unless exps.empty? + return call_exps end - node.args.each { |arg| exps.push arg } - call_exps = Expressions.from exps - call_exps.set_type(exps.last.type?) unless exps.empty? - return call_exps end node.replace_splats @@ -1068,7 +1069,10 @@ module Crystal node = super unless node.type? - node.unbind_from node.dependencies + if dependencies = node.dependencies? + node.unbind_from node.dependencies + end + node.bind_to node.expressions end diff --git a/src/compiler/crystal/semantic/cover.cr b/src/compiler/crystal/semantic/cover.cr index 1ae936cba44c..4674d0a12248 100644 --- a/src/compiler/crystal/semantic/cover.cr +++ b/src/compiler/crystal/semantic/cover.cr @@ -4,9 +4,9 @@ require "../types" module Crystal struct Cover getter signature : CallSignature - getter matches : ZeroOneOrMany(Match) + getter matches : Array(Match) - def self.create(signature : CallSignature, matches : ZeroOneOrMany(Match)) + def self.create(signature, matches) if matches matches.empty? ? false : Cover.new(signature, matches) else @@ -14,7 +14,7 @@ module Crystal end end - def initialize(@signature : CallSignature, @matches : ZeroOneOrMany(Match)) + def initialize(@signature, @matches) end def all? diff --git a/src/compiler/crystal/semantic/filters.cr b/src/compiler/crystal/semantic/filters.cr index ebc77d01e675..86938b7e7b77 100644 --- a/src/compiler/crystal/semantic/filters.cr +++ b/src/compiler/crystal/semantic/filters.cr @@ -1,7 +1,7 @@ module Crystal class TypeFilteredNode < ASTNode def initialize(@filter : TypeFilter, @node : ASTNode) - @dependencies = ZeroOneOrMany.new(@node) + @dependencies = [@node] of ASTNode node.add_observer self update(@node) end diff --git a/src/compiler/crystal/semantic/fix_missing_types.cr b/src/compiler/crystal/semantic/fix_missing_types.cr index 07a113c50d20..3a0eb5857d1d 100644 --- a/src/compiler/crystal/semantic/fix_missing_types.cr +++ b/src/compiler/crystal/semantic/fix_missing_types.cr @@ -74,7 +74,7 @@ class Crystal::FixMissingTypes < Crystal::Visitor block.type = @program.no_return end - node.target_defs.each do |target_def| + node.target_defs.try &.each do |target_def| if @fixed.add?(target_def) target_def.type = @program.no_return unless target_def.type? target_def.accept_children self diff --git a/src/compiler/crystal/semantic/lib.cr b/src/compiler/crystal/semantic/lib.cr index f81f63226795..5e0bff6ce3ad 100644 --- a/src/compiler/crystal/semantic/lib.cr +++ b/src/compiler/crystal/semantic/lib.cr @@ -23,10 +23,11 @@ class Crystal::Call obj_type.used = true external.used = true - @target_defs = ZeroOneOrMany(Def).new(external) + untyped_defs = [external] of Def + @target_defs = untyped_defs - self.unbind_from old_target_defs unless old_target_defs.empty? - self.bind_to external + self.unbind_from old_target_defs if old_target_defs + self.bind_to untyped_defs end def check_lib_call_named_args(external) diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index 44da862b2a1d..e3c710134e1e 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -370,7 +370,8 @@ module Crystal var.bind_to(@program.nil_var) var.nil_if_read = false - bind_to_program_nil_var(meta_var) + meta_var.bind_to(@program.nil_var) unless meta_var.dependencies.try &.any? &.same?(@program.nil_var) + node.bind_to(@program.nil_var) end check_mutably_closured meta_var, var @@ -395,10 +396,6 @@ module Crystal end end - private def bind_to_program_nil_var(node) - node.bind_to(@program.nil_var) unless node.dependencies.any? &.same?(@program.nil_var) - end - def visit(node : TypeDeclaration) case var = node.var when Var @@ -600,6 +597,19 @@ module Crystal program.undefined_instance_variable(node, owner, similar_name) end + def first_time_accessing_meta_type_var?(var) + return false if var.uninitialized? + + if var.freeze_type + deps = var.dependencies? + # If no dependencies, it's the case of a global for a regex literal. + # If there are dependencies and it's just one, it's the same var + deps ? deps.size == 1 : false + else + !var.dependencies? + end + end + def visit(node : InstanceVar) var = lookup_instance_var node node.bind_to(var) @@ -1242,7 +1252,7 @@ module Crystal # It can happen that this call is inside an ArrayLiteral or HashLiteral, # was expanded but isn't bound to the expansion because the call (together # with its expansion) was cloned. - if (expanded = node.expanded) && (node.dependencies.empty? || !node.type?) + if (expanded = node.expanded) && (!node.dependencies? || !node.type?) node.bind_to(expanded) end @@ -3246,7 +3256,7 @@ module Crystal def define_special_var(name, value) meta_var, _ = assign_to_meta_var(name) meta_var.bind_to value - bind_to_program_nil_var(meta_var) + meta_var.bind_to program.nil_var unless meta_var.dependencies.any? &.same?(program.nil_var) meta_var.assigned_to = true check_closured meta_var diff --git a/src/compiler/crystal/semantic/match.cr b/src/compiler/crystal/semantic/match.cr index 0e282e497d42..f18572f69291 100644 --- a/src/compiler/crystal/semantic/match.cr +++ b/src/compiler/crystal/semantic/match.cr @@ -130,7 +130,7 @@ module Crystal struct Matches include Enumerable(Match) - property matches : ZeroOneOrMany(Match) + property matches : Array(Match)? property cover : Bool | Cover | Nil property owner : Type? @@ -139,25 +139,32 @@ module Crystal def cover_all? cover = @cover - @success && !@matches.empty? && (cover == true || (cover.is_a?(Cover) && cover.all?)) + matches = @matches + @success && matches && matches.size > 0 && (cover == true || (cover.is_a?(Cover) && cover.all?)) end def empty? - !@success || matches.empty? + return true unless @success + + if matches = @matches + matches.empty? + else + true + end end def each - @success && @matches.each do |match| + @success && @matches.try &.each do |match| yield match end end def size - @matches.size + @matches.try(&.size) || 0 end def [](*args) - Matches.new(@matches.[*args], @cover, @owner, @success) + Matches.new(@matches.try &.[](*args), @cover, @owner, @success) end end end diff --git a/src/compiler/crystal/semantic/method_lookup.cr b/src/compiler/crystal/semantic/method_lookup.cr index c7f1e8087507..9c5e42c12ac0 100644 --- a/src/compiler/crystal/semantic/method_lookup.cr +++ b/src/compiler/crystal/semantic/method_lookup.cr @@ -66,7 +66,7 @@ module Crystal named_args : Array(NamedArgumentType)? class Type - def lookup_matches(signature, owner = self, path_lookup = self, matches_array = ZeroOneOrMany(Match).new, analyze_all = false) + def lookup_matches(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false) matches = lookup_matches_without_parents(signature, owner, path_lookup, matches_array, analyze_all: analyze_all) return matches if matches.cover_all? @@ -105,7 +105,7 @@ module Crystal Matches.new(matches_array, cover, owner, false) end - def lookup_matches_without_parents(signature, owner = self, path_lookup = self, matches_array = ZeroOneOrMany(Match).new, analyze_all = false) + def lookup_matches_without_parents(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false) if defs = self.defs.try &.[signature.name]? context = MatchContext.new(owner, path_lookup) @@ -129,7 +129,8 @@ module Crystal next if exact_match if match - matches_array.push match + matches_array ||= [] of Match + matches_array << match # If the argument types are compatible with the match's argument types, # we are done. We don't just compare types with ==, there is a special case: @@ -157,7 +158,7 @@ module Crystal Matches.new(matches_array, Cover.create(signature, matches_array), owner) end - def lookup_matches_with_modules(signature, owner = self, path_lookup = self, matches_array = ZeroOneOrMany(Match).new, analyze_all = false) + def lookup_matches_with_modules(signature, owner = self, path_lookup = self, matches_array = nil, analyze_all = false) matches = lookup_matches_without_parents(signature, owner, path_lookup, matches_array, analyze_all: analyze_all) return matches unless matches.empty? @@ -439,7 +440,7 @@ module Crystal if is_new && subtype_matches.empty? other_initializers = subtype_lookup.instance_type.lookup_defs_with_modules("initialize") unless other_initializers.empty? - return Matches.new(ZeroOneOrMany(Match).new, false) + return Matches.new(nil, false) end end @@ -447,7 +448,7 @@ module Crystal # def, we need to copy it to the subclass so that @name, @instance_vars and other # macro vars resolve correctly. if subtype_matches.empty? - new_subtype_matches = ZeroOneOrMany(Match).new + new_subtype_matches = nil base_type_matches.each do |base_type_match| if base_type_match.def.macro_def? @@ -479,6 +480,7 @@ module Crystal changes << Change.new(change_owner, cloned_def) end + new_subtype_matches ||= [] of Match new_subtype_matches.push Match.new(cloned_def, full_subtype_match.arg_types, MatchContext.new(subtype_lookup, full_subtype_match.context.defining_type, full_subtype_match.context.free_vars), full_subtype_match.named_arg_types) end @@ -487,7 +489,7 @@ module Crystal end end - unless new_subtype_matches.empty? + if new_subtype_matches subtype_matches = Matches.new(new_subtype_matches, Cover.create(signature, new_subtype_matches)) end end diff --git a/src/compiler/crystal/semantic/type_merge.cr b/src/compiler/crystal/semantic/type_merge.cr index 9b2fe4e236f5..0d79faee06b1 100644 --- a/src/compiler/crystal/semantic/type_merge.cr +++ b/src/compiler/crystal/semantic/type_merge.cr @@ -2,7 +2,7 @@ require "../program" module Crystal class Program - def type_merge(types : Indexable(Type?)) : Type? + def type_merge(types : Array(Type?)) : Type? case types.size when 0 nil @@ -17,7 +17,7 @@ module Crystal end end - def type_merge(nodes : Indexable(ASTNode)) : Type? + def type_merge(nodes : Array(ASTNode)) : Type? case nodes.size when 0 nil @@ -161,11 +161,11 @@ module Crystal end class Type - def self.merge(nodes : Indexable(ASTNode)) : Type? + def self.merge(nodes : Array(ASTNode)) : Type? nodes.find(&.type?).try &.type.program.type_merge(nodes) end - def self.merge(types : Indexable(Type)) : Type? + def self.merge(types : Array(Type)) : Type? if types.size == 0 nil else diff --git a/src/compiler/crystal/semantic/warnings.cr b/src/compiler/crystal/semantic/warnings.cr index 437632bc9953..0dae71c5cd06 100644 --- a/src/compiler/crystal/semantic/warnings.cr +++ b/src/compiler/crystal/semantic/warnings.cr @@ -26,7 +26,7 @@ module Crystal return unless @warnings.level.all? return if compiler_expanded_call(node) - node.target_defs.each do |target_def| + node.target_defs.try &.each do |target_def| check_deprecation(target_def, node, @deprecated_methods_detected) end end diff --git a/src/compiler/crystal/tools/context.cr b/src/compiler/crystal/tools/context.cr index 7bf49397fd7c..5bc6dd43e0e9 100644 --- a/src/compiler/crystal/tools/context.cr +++ b/src/compiler/crystal/tools/context.cr @@ -77,11 +77,13 @@ module Crystal def visit(node : Call) return false if node.obj.nil? && node.name == "raise" - node.target_defs.each do |typed_def| - typed_def.accept(self) - next unless @context_visitor.def_with_yield.not_nil!.location == typed_def.location - @context_visitor.inside_typed_def do - typed_def.accept(@context_visitor) + node.target_defs.try do |defs| + defs.each do |typed_def| + typed_def.accept(self) + next unless @context_visitor.def_with_yield.not_nil!.location == typed_def.location + @context_visitor.inside_typed_def do + typed_def.accept(@context_visitor) + end end end true diff --git a/src/compiler/crystal/tools/implementations.cr b/src/compiler/crystal/tools/implementations.cr index 4e49f18c4712..f4f3a390f0eb 100644 --- a/src/compiler/crystal/tools/implementations.cr +++ b/src/compiler/crystal/tools/implementations.cr @@ -109,8 +109,10 @@ module Crystal def visit(node : Call) return contains_target(node) unless node.location && @target_location.between?(node.name_location, node.name_end_location) - node.target_defs.each do |target_def| - @locations << target_def.location.not_nil! + if target_defs = node.target_defs + target_defs.each do |target_def| + @locations << target_def.location.not_nil! + end end end diff --git a/src/compiler/crystal/zero_one_or_many.cr b/src/compiler/crystal/zero_one_or_many.cr deleted file mode 100644 index 80c1557e09cb..000000000000 --- a/src/compiler/crystal/zero_one_or_many.cr +++ /dev/null @@ -1,117 +0,0 @@ -# An Array(T)-like type that's optimized for the case of -# frequently having zero or one elements. -struct Crystal::ZeroOneOrMany(T) - include Indexable(T) - - getter value : Nil | T | Array(T) - - def initialize - @value = nil - end - - def initialize(@value : T) - end - - def unsafe_fetch(index : Int) - value = @value - case value - in Nil - raise IndexError.new("Called ZeroOneOrMany#unsafe_fetch but value is nil") - in T - if index != 0 - raise IndexError.new("Called ZeroOneOrMany#unsafe_fetch with index != 0 but value is not an array") - end - - value - in Array(T) - value.unsafe_fetch(index) - end - end - - def each(& : T ->) - value = @value - case value - in Nil - # Nothing to do - in T - yield value - in Array(T) - value.each do |element| - yield element - end - end - end - - def size : Int32 - value = @value - case value - in Nil - 0 - in T - 1 - in Array(T) - value.size - end - end - - def <<(element : T) : self - push(element) - end - - def push(element : T) : self - value = @value - case value - in Nil - @value = element - in T - @value = [value, element] of T - in Array(T) - value.push element - end - self - end - - def concat(elements : Indexable(T)) : self - value = @value - case value - in Nil - case elements.size - when 0 - # Nothing to do - when 1 - @value = elements.first - else - @value = elements.map(&.as(T)).to_a - end - in T - new_value = Array(T).new(elements.size + 1) - new_value.push(value) - new_value.concat(elements) - @value = new_value - in Array(T) - value.concat(elements) - end - self - end - - def reject!(&block : T -> _) : self - value = @value - case value - in Nil - # Nothing to do - in T - if yield value - @value = nil - end - in Array(T) - value.reject! { |element| yield element } - case value.size - when 0 - @value = nil - when 1 - @value = value.first - end - end - self - end -end