module Steep module Interface class Instantiated attr_reader :type attr_reader :methods attr_reader :ivar_chains def initialize(type:, methods:, ivar_chains:) @type = type @methods = methods @ivar_chains = ivar_chains end def ivars @ivars ||= ivar_chains.transform_values(&:type) end def ==(other) other.is_a?(self.class) && other.type == type && other.params == params && other.methods == methods && other.ivars == ivars end def subst(s) self.class.new( type: type, methods: methods.transform_values {|type| type.subst(s) }, ivar_chains: ivar_chains.transform_values {|chain| chain.subst(s) } ) end class InvalidMethodOverrideError < StandardError attr_reader :type attr_reader :current_method attr_reader :super_method attr_reader :result def initialize(type:, current_method:, super_method:, result:) @type = type @current_method = current_method @super_method = super_method @result = result super "Invalid override of `#{current_method.name}` in #{type}: definition in #{current_method.type_name} is not compatible with its super (#{super_method.type_name})" end end class InvalidIvarOverrideError < StandardError attr_reader :type attr_reader :ivar_name attr_reader :current_ivar_type attr_reader :super_ivar_type def initialize(type:, ivar_name:, current_ivar_type:, super_ivar_type:) @type = type @ivar_name = ivar_name @current_ivar_type = current_ivar_type @super_ivar_type = super_ivar_type super "Invalid override of `#{ivar_name}` in #{type}: #{current_ivar_type} is not compatible with #{super_ivar_type}" end end class PrivateOverrideError < StandardError attr_reader :child_type attr_reader :super_type attr_reader :method_name def initialize(child_type:, super_type:, method_name:) @child_type = child_type @super_type = super_type @method_name = method_name super "Public method `#{method_name}` cannot overriden to private: #{child_type} <: #{super_type}" end end def validate(check) methods.each do |_, method| validate_method_type(check, method) validate_method_visibility method end ivar_chains.each do |name, chain| validate_chain(check, name, chain) end end def validate_chain(check, name, chain) return unless chain.parent this_type = chain.type super_type = chain.parent.type case when this_type.is_a?(AST::Types::Any) && super_type.is_a?(AST::Types::Any) # ok else relation = Subtyping::Relation.new(sub_type: this_type, super_type: super_type) result1 = check.check(relation, constraints: Subtyping::Constraints.empty) result2 = check.check(relation.flip, constraints: Subtyping::Constraints.empty) if result1.failure? || result2.failure? || this_type.is_a?(AST::Types::Any) || super_type.is_a?(AST::Types::Any) raise InvalidIvarOverrideError.new(type: self.type, ivar_name: name, current_ivar_type: this_type, super_ivar_type: super_type) end end validate_chain(check, name, chain.parent) end def validate_method_type(check, method) if method.super_method && !method.incompatible? result = check.check_method(method.name, method, method.super_method, assumption: Set.new, trace: Subtyping::Trace.new, constraints: Subtyping::Constraints.empty) if result.success? validate_method_type(check, method.super_method) else raise InvalidMethodOverrideError.new(type: type, current_method: method, super_method: method.super_method, result: result) end end end def validate_method_visibility(method) if (super_method = method.super_method) if method.private? && !super_method.private? raise PrivateOverrideError.new( child_type: method.type_name, super_type: super_method.type_name, method_name: method.name ) end validate_method_visibility(super_method) end end def select_method_type(&block) self.class.new( type: type, methods: methods.each.with_object({}) do |(name, method), methods| methods[name] = Method.new( type_name: method.type_name, name: method.name, types: method.types.select(&block), super_method: method.super_method, attributes: method.attributes, ) end.reject do |_, method| method.types.empty? end, ivar_chains: ivar_chains ) end end end end