class TypedRb::Model::TmSend
message send
Attributes
args[RW]
block[RW]
message[RW]
receiver[RW]
Public Class Methods
new(receiver, message, args, node)
click to toggle source
Calls superclass method
TypedRb::Model::Expr::new
# File lib/typed/model/tm_send.rb, line 8 def initialize(receiver, message, args, node) super(node) @receiver = receiver @message = message @args = args @block = nil end
Public Instance Methods
cast?(function_klass_type)
click to toggle source
# File lib/typed/model/tm_send.rb, line 264 def cast?(function_klass_type) function_klass_type == BasicObject && message == :cast end
check_application(receiver_type, function_type, context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 162 def check_application(receiver_type, function_type, context) if function_type.is_a?(Types::TyDynamicFunction) function_type.to else if function_type.generic? function_type.local_typing_context.parent = Types::TypingContext.type_variables_register return_type = function_type.materialize do |materialized_function| check_application(receiver_type, materialized_function, context) end.to return_type.respond_to?(:as_object_type) ? return_type.as_object_type : return_type else formal_parameters = function_type.from parameters_info = function_type.parameters_info TypedRb.log(binding, :debug, "Checking function application #{receiver_type}::#{message}( #{parameters_info} )") check_args_application(parameters_info, formal_parameters, args, context) if @block block_type = @block.check_type(context) # TODO: # Unification is run here # Algorithm is failing: # G > String, # G < E # ======== # G = [String, ?] # ----- # G = [String, E] # E = [String, ?] block_return_type = if function_type.block_type # materialization and unification will happen in this invocation block_type.compatible?(function_type.block_type, :lt) else block_type.to end if block_return_type.to.stack_jump? break_type = block_return_type.to.wrapped_type.check_type(context) unless break_type.compatible?(function_type.to, :lt) error_message = "Incompatible 'break' type, expected #{function_type.to}, found #{break_type}" fail error_message, block_return_type.to.node end elsif block_return_type.to.either? max_type = block_return_type.check_type(context, [:return, :break, :normal]) unless max_type.compatible?(function_type.to, :lt) error_message = "Incompatible either max type, expected #{function_type.to}, found #{max_type}" fail error_message, block_return_type.to.node end end end return_type = function_type.to return_type.respond_to?(:as_object_type) ? return_type.as_object_type : return_type end end end
check_args_application(parameters_info, formal_parameters, actual_arguments, context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 219 def check_args_application(parameters_info, formal_parameters, actual_arguments, context) #binding.pry if actual_arguments.size == 1 && actual_arguments.first.class == TypedRb::Model::TmVar && actual_arguments.first.val == "klass" && actual_arguments.first.col == 36 parameters_info.each_with_index do |(require_info, arg_name), index| actual_argument = actual_arguments[index] formal_parameter_type = formal_parameters[index] if formal_parameter_type.nil? && !require_info == :block fail TypeCheckError.new("Error type checking message sent '#{message}': Missing information about argument #{arg_name} in #{receiver}##{message}", node) end if actual_argument.nil? && require_info != :opt && require_info != :rest && require_info != :block fail TypeCheckError.new("Error type checking message sent '#{message}': Missing mandatory argument #{arg_name} in #{receiver}##{message}", node) else if require_info == :rest break if actual_argument.nil? # invocation without any of the optional arguments rest_type = formal_parameter_type.type_vars.first formal_parameter_type = if rest_type.respond_to?(:bound) rest_type.bound else rest_type end actual_arguments[index..-1].each do |actual_argument| actual_argument_type = actual_argument.check_type(context) unless actual_argument_type.compatible?(formal_parameter_type, :lt) error_message = "Error type checking message sent '#{message}': #{formal_parameter_type} expected, #{actual_argument_type} found" fail TypeCheckError.new(error_message, node) end end break else unless actual_argument.nil? # opt or block if this is nil actual_argument_type = actual_argument.check_type(context) fail TypeCheckError.new("Error type checking message sent '#{message}': Missing type information for argument '#{arg_name}'", node) if formal_parameter_type.nil? begin unless actual_argument_type.compatible?(formal_parameter_type, :lt) error_message = "Error type checking message sent '#{message}': #{formal_parameter_type} expected, #{actual_argument_type} found" fail TypeCheckError.new(error_message, node) end rescue Types::UncomparableTypes, ArgumentError raise Types::UncomparableTypes.new(actual_argument_type, formal_parameter_type, node) end end end end end end
check_casting(context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 268 def check_casting(context) from_type = args[0].check_type(context) to = parse_type_application_arguments([args[1]], context).first to_type = to.is_a?(Types::TyObject) ? to.as_object_type : to TypedRb.log(binding, :info, "Casting #{from_type} into #{to_type}") to_type end
check_instantiation(context)
click to toggle source
we received new, but we look for initialize in the class, not the singleton class. we then run the regular application, but we return the class type instead of the return type for the constructor application (should be unit/nil).
# File lib/typed/model/tm_send.rb, line 51 def check_instantiation(context) self_type = singleton_object_type(receiver, context).as_object_type function_klass_type, function_type = self_type.find_function_type(:initialize, args.size, @block) TypedRb.log_dynamic_warning(node, self_type, :initialize) if function_type.dynamic? # function application @message = :initialize begin check_application(self_type, function_type, context) rescue TypeCheckError => error raise error if function_klass_type == self_type.ruby_type end self_type end
check_lambda_application(lambda_type, context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 215 def check_lambda_application(lambda_type, context) lambda_type.check_args_application(args, context).to end
check_module_inclusions(self_type, context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 280 def check_module_inclusions(self_type, context) args.map do |arg| arg.check_type(context) end.each do |module_type| if module_type.is_a?(Types::TyExistentialType) if module_type.local_typing_context module_type.check_inclusion(self_type) else # TODO: report warning about missing module information TypedRb.log(binding, :debug, "Not type checking module #{module_type.ruby_type} inclusion due to lack of module information") end else error_message = "Error type checking message sent '#{message}': Module type expected for inclusion in #{self_type}, #{module_type} found" fail TypeCheckError.new(error_message, node) end end self_type end
check_type(context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 20 def check_type(context) @context = context TypedRb.log(binding, :debug, "Type checking message sent: #{message} at line #{node.loc.line}") if receiver.nil? && message == :ts # ignore, => type annotation Types::TyUnit.new(node) elsif message == :new && !singleton_object_type(receiver, context).nil? # clean this! check_instantiation(context) elsif receiver == :self || receiver.nil? # self.m(args), m(args), m check_type_no_explicit_receiver(context) else # x.m(args) check_type_explicit_receiver(context) end end
check_type_application_to_generic(generic_type, args)
click to toggle source
# File lib/typed/model/tm_send.rb, line 158 def check_type_application_to_generic(generic_type, args) generic_type.materialize(args) end
check_type_explicit_receiver(context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 84 def check_type_explicit_receiver(context) if receiver_type.is_a?(Types::Polymorphism::TypeVariable) # Existential type (Module) if receiver_type is self # TODO: what can we do if this is the inclusion of a module? arg_types = args.map { |arg| arg.check_type(context) } receiver_type.add_message_constraint(message, arg_types) elsif receiver_type.is_a?(Types::TyGenericSingletonObject) && (message == :call) # Application of types accept a type class or a string with a type description arg_types = parse_type_application_arguments(args, context) check_type_application_to_generic(receiver_type, arg_types) elsif receiver_type.is_a?(Types::TyFunction) && (message == :[] || message == :call) check_lambda_application(receiver_type, context) else function_klass_type, function_type = receiver_type.find_function_type(message, args.size, @block) TypedRb.log_dynamic_warning(node, receiver_type, message) if function_type.dynamic? # begin if function_type.nil? error_message = "Error type checking message sent '#{message}': Type information for #{receiver_type}:#{message} not found." fail TypeCheckError.new(error_message, node) elsif cast?(function_klass_type) check_casting(context) elsif module_include_implementation?(function_klass_type) check_module_inclusions(receiver_type, context) else # function application check_application(receiver_type, function_type, context) end # rescue TypeCheckError => error # if function_klass_type != receiver_type.ruby_type # Types::TyDynamic.new(Object, node) # else # raise error # end # end end end
check_type_no_explicit_receiver(context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 66 def check_type_no_explicit_receiver(context) if message == :yield check_yield_application(context) else @receiver_type = context.get_type_for(:self) # check message in self type -> application check_type_explicit_receiver(context) end end
check_yield_application(context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 75 def check_yield_application(context) yield_abs_type = context.get_type_for(:yield) if yield_abs_type check_lambda_application(yield_abs_type, context) else fail TypeCheckError.new("Error type checking message sent '#{message}': Cannot find yield function defined in typing context", node) end end
module_include_implementation?(function_klass_type)
click to toggle source
# File lib/typed/model/tm_send.rb, line 276 def module_include_implementation?(function_klass_type) function_klass_type == Module && message == :include end
parse_type_application_argument(type)
click to toggle source
# File lib/typed/model/tm_send.rb, line 144 def parse_type_application_argument(type) # TODO: do this recursively in the case of nested generic type # TODO: do we need it at all? klass = if type.is_a?(Hash) && type[:kind] == :generic_type Class.for_name(type[:type]) end Runtime::TypeParser.parse(type, klass) end
parse_type_application_arguments(arguments, context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 121 def parse_type_application_arguments(arguments, context) arguments.map do |argument| if argument.is_a?(Model::TmString) type_var_signature = argument.node.children.first maybe_generic_method_var = Types::TypingContext.vars_info(:method)[type_var_signature] maybe_generic_class_var = Types::TypingContext.vars_info(:class)[type_var_signature] maybe_generic_module_var = Types::TypingContext.vars_info(:module)[type_var_signature] if maybe_generic_method_var || maybe_generic_class_var || maybe_generic_module_var maybe_generic_method_var || maybe_generic_class_var || maybe_generic_module_var else parsed_types = TypeSignature::Parser.parse(type_var_signature) if parsed_types.is_a?(Array) parsed_types.map { |parsed_type| parse_type_application_argument(parsed_type) } else parse_type_application_argument(parsed_types) end end else argument.check_type(context) end end.flatten end
receiver_type()
click to toggle source
# File lib/typed/model/tm_send.rb, line 299 def receiver_type @receiver_type ||= receiver.check_type(@context) end
singleton_object_type(receiver, context)
click to toggle source
# File lib/typed/model/tm_send.rb, line 37 def singleton_object_type(receiver, context) parsed_receiver_type = if receiver.nil? || receiver == :self context.get_type_for(:self) else receiver_type end return parsed_receiver_type if parsed_receiver_type.is_a?(Types::TySingletonObject) end
type_application_counter()
click to toggle source
# File lib/typed/model/tm_send.rb, line 153 def type_application_counter @type_application_counter ||= 0 @type_application_counter += 1 end
with_block(block)
click to toggle source
# File lib/typed/model/tm_send.rb, line 16 def with_block(block) @block = block end