lib/rbs/definition_builder.rb in rbs-0.4.0 vs lib/rbs/definition_builder.rb in rbs-0.5.0

- old
+ new

@@ -1,190 +1,212 @@ module RBS class DefinitionBuilder attr_reader :env + attr_reader :type_name_resolver + attr_reader :instance_cache attr_reader :singleton_cache + attr_reader :interface_cache + attr_reader :one_instance_cache + attr_reader :one_singleton_cache + + attr_reader :instance_ancestors_cache + attr_reader :singleton_ancestor_cache + def initialize(env:) @env = env + @type_name_resolver = TypeNameResolver.from_env(env) + @instance_cache = {} @singleton_cache = {} + @interface_cache = {} + + @one_instance_cache = {} + @one_singleton_cache = {} + + @instance_ancestors_cache = {} + @singleton_ancestor_cache = {} end - def build_ancestors(self_ancestor, ancestors: [], building_ancestors: [], location: nil) - decl = env.find_class(self_ancestor.name) - namespace = self_ancestor.name.absolute!.to_namespace + def validate_super_class!(type_name, entry) + with_super_classes = entry.decls.select {|d| d.decl.super_class } + return if with_super_classes.size <= 1 + + super_types = with_super_classes.map do |d| + super_class = d.decl.super_class + Types::ClassInstance.new(name: super_class.name, args: super_class.args, location: nil) + end + + super_types.uniq! + + return if super_types.size == 1 + + raise SuperclassMismatchError.new(name: type_name, super_classes: super_types, entry: entry) + end + + def instance_ancestors(type_name, building_ancestors: []) + as = instance_ancestors_cache[type_name] and return as + + entry = env.class_decls[type_name] + params = entry.type_params.each.map(&:name) + args = Types::Variable.build(params) + self_ancestor = Definition::Ancestor::Instance.new(name: type_name, args: args) + RecursiveAncestorError.check!(self_ancestor, ancestors: building_ancestors, - location: location || decl.location) + location: entry.primary.decl.location) building_ancestors.push self_ancestor - case self_ancestor - when Definition::Ancestor::Instance - args = self_ancestor.args - param_names = decl.type_params.each.map(&:name) + ancestors = [] - InvalidTypeApplicationError.check!( - type_name: self_ancestor.name, - args: args, - params: decl.type_params, - location: location || decl.location - ) + case entry + when Environment::ClassEntry + validate_super_class!(type_name, entry) - sub = Substitution.build(param_names, args) + # Super class comes last + if self_ancestor.name != BuiltinNames::BasicObject.name + primary = entry.primary + super_class = primary.decl.super_class - case decl - when AST::Declarations::Class - unless self_ancestor.name == BuiltinNames::BasicObject.name - super_ancestor = decl.super_class&.yield_self do |super_class| - Definition::Ancestor::Instance.new( - name: absolute_type_name(super_class.name, namespace: namespace, location: location || decl.location), - args: super_class.args.map {|ty| absolute_type(ty.sub(sub), namespace: namespace) } - ) - end || Definition::Ancestor::Instance.new(name: BuiltinNames::Object.name, args: []) - - build_ancestors(super_ancestor, ancestors: ancestors, building_ancestors: building_ancestors) + if super_class + super_name = super_class.name + super_args = super_class.args + else + super_name = BuiltinNames::Object.name + super_args = [] end - end - decl.members.each do |member| - case member - when AST::Members::Include - if member.name.class? - ancestor = Definition::Ancestor::Instance.new( - name: absolute_type_name(member.name, namespace: namespace, location: member.location), - args: member.args.map {|ty| absolute_type(ty.sub(sub), namespace: namespace) } - ) - build_ancestors ancestor, ancestors: ancestors, building_ancestors: building_ancestors, location: member.location - end - end + super_ancestors = instance_ancestors(super_name, building_ancestors: building_ancestors) + ancestors.unshift(*super_ancestors.apply(super_args, location: primary.decl.location)) end - ancestors.unshift(self_ancestor) + build_ancestors_mixin_self(self_ancestor, entry, ancestors: ancestors, building_ancestors: building_ancestors) - env.each_extension(self_ancestor.name).sort_by {|e| e.extension_name.to_s }.each do |extension| - InvalidExtensionParameterError.check!( - type_name: self_ancestor.name, - extension_name: extension.extension_name, - extension_params: extension.type_params, - class_params: self_ancestor.args.map(&:name), - location: extension.location - ) + when Environment::ModuleEntry + build_ancestors_mixin_self(self_ancestor, entry, ancestors: ancestors, building_ancestors: building_ancestors) - sub = Substitution.build(extension.type_params, self_ancestor.args) + end - extension.members.each do |member| - case member - when AST::Members::Include - if member.name.class? - ancestor = Definition::Ancestor::Instance.new( - name: absolute_type_name(member.name, namespace: namespace, location: member.location), - args: member.args.map {|ty| absolute_type(ty.sub(sub), namespace: namespace) } - ) - build_ancestors ancestor, ancestors: ancestors, building_ancestors: building_ancestors, location: member.location - end - end - end + building_ancestors.pop - extension_ancestor = Definition::Ancestor::ExtensionInstance.new( - name: extension.name.absolute!, - extension_name: extension.extension_name, - args: self_ancestor.args, - ) - ancestors.unshift(extension_ancestor) + instance_ancestors_cache[type_name] = Definition::InstanceAncestors.new( + type_name: type_name, + params: params, + ancestors: ancestors + ) + end - extension.members.each do |member| - case member - when AST::Members::Prepend - if member.name.class? - ancestor = Definition::Ancestor::Instance.new( - name: absolute_type_name(member.name, namespace: namespace, location: member.location), - args: member.args.map {|ty| absolute_type(ty.sub(sub), namespace: namespace) } - ) - build_ancestors ancestor, ancestors: ancestors, building_ancestors: building_ancestors, location: member.location - end - end - end - end + def singleton_ancestors(type_name, building_ancestors: []) + as = singleton_ancestor_cache[type_name] and return as - decl.members.each do |member| - case member - when AST::Members::Prepend - ancestor = Definition::Ancestor::Instance.new( - name: absolute_type_name(member.name, namespace: namespace, location: member.location), - args: member.args.map {|ty| absolute_type(ty.sub(sub), namespace: namespace) } - ) - build_ancestors ancestor, ancestors: ancestors, building_ancestors: building_ancestors, location: member.location - end - end - when Definition::Ancestor::Singleton - case decl - when AST::Declarations::Class - if self_ancestor.name == BuiltinNames::BasicObject.name - class_ancestor = Definition::Ancestor::Instance.new( - name: BuiltinNames::Class.name, - args: [] - ) - build_ancestors class_ancestor, ancestors: ancestors, building_ancestors: building_ancestors - else - super_ancestor = decl.super_class&.yield_self do |super_class| - Definition::Ancestor::Singleton.new( - name: absolute_type_name(super_class.name, namespace: namespace, location: location || decl.location) - ) - end || Definition::Ancestor::Singleton.new(name: BuiltinNames::Object.name) + entry = env.class_decls[type_name] + self_ancestor = Definition::Ancestor::Singleton.new(name: type_name) - build_ancestors(super_ancestor, ancestors: ancestors, building_ancestors: building_ancestors) + RecursiveAncestorError.check!(self_ancestor, + ancestors: building_ancestors, + location: entry.primary.decl.location) + building_ancestors.push self_ancestor + + ancestors = [] + + case entry + when Environment::ClassEntry + # Super class comes last + if self_ancestor.name != BuiltinNames::BasicObject.name + primary = entry.primary + super_class = primary.decl.super_class + + if super_class + super_name = super_class.name + else + super_name = BuiltinNames::Object.name end - when AST::Declarations::Module - module_ancestor = Definition::Ancestor::Instance.new( - name: BuiltinNames::Module.name, - args: [] - ) - build_ancestors module_ancestor, ancestors: ancestors, building_ancestors: building_ancestors + + super_ancestors = singleton_ancestors(super_name, building_ancestors: building_ancestors) + ancestors.unshift(*super_ancestors.ancestors) + else + as = instance_ancestors(BuiltinNames::Class.name, building_ancestors: building_ancestors) + ancestors.unshift(*as.apply([], location: entry.primary.decl.location)) end - decl.members.each do |member| + when Environment::ModuleEntry + as = instance_ancestors(BuiltinNames::Module.name, building_ancestors: building_ancestors) + ancestors.unshift(*as.apply([], location: entry.primary.decl.location)) + end + + # Extend comes next + entry.decls.each do |d| + decl = d.decl + + decl.each_mixin do |member| case member when AST::Members::Extend if member.name.class? - ancestor = Definition::Ancestor::Instance.new( - name: absolute_type_name(member.name, namespace: namespace, location: member.location), - args: member.args.map {|ty| absolute_type(ty.sub(sub), namespace: namespace) } - ) - build_ancestors ancestor, ancestors: ancestors, building_ancestors: building_ancestors, location: member.location + module_ancestors = instance_ancestors(member.name, building_ancestors: building_ancestors) + ancestors.unshift(*module_ancestors.apply(member.args, location: member.location)) end end end + end - ancestors.unshift(self_ancestor) + ancestors.unshift self_ancestor - env.each_extension(self_ancestor.name).sort_by {|e| e.extension_name.to_s }.each do |extension| - extension.members.each do |member| - case member - when AST::Members::Extend - if member.name.class? - ancestor = Definition::Ancestor::Instance.new( - name: absolute_type_name(member.name, namespace: namespace, location: member.location), - args: member.args.map {|ty| absolute_type(ty, namespace: namespace) } - ) - build_ancestors ancestor, ancestors: ancestors, building_ancestors: building_ancestors, location: member.location - end + building_ancestors.pop + + singleton_ancestor_cache[type_name] = Definition::SingletonAncestors.new( + type_name: type_name, + ancestors: ancestors + ) + end + + def build_ancestors_mixin_self(self_ancestor, entry, ancestors:, building_ancestors:) + # Include comes next + entry.decls.each do |d| + decl = d.decl + + align_params = Substitution.build( + decl.type_params.each.map(&:name), + Types::Variable.build(entry.type_params.each.map(&:name)) + ) + + decl.each_mixin do |member| + case member + when AST::Members::Include + if member.name.class? + module_name = member.name + module_args = member.args.map {|type| type.sub(align_params) } + + module_ancestors = instance_ancestors(module_name, building_ancestors: building_ancestors) + ancestors.unshift(*module_ancestors.apply(module_args, location: member.location)) end end - - extension_ancestor = Definition::Ancestor::ExtensionSingleton.new( - name: extension.name.absolute!, - extension_name: extension.extension_name - ) - ancestors.unshift(extension_ancestor) end end - building_ancestors.pop + # Self + ancestors.unshift(self_ancestor) - ancestors + # Prepends + entry.decls.each do |d| + decl = d.decl + + align_params = Substitution.build(decl.type_params.each.map(&:name), + Types::Variable.build(entry.type_params.each.map(&:name))) + + decl.each_mixin do |member| + case member + when AST::Members::Prepend + module_name = member.name + module_args = member.args.map {|type| type.sub(align_params) } + + module_ancestors = instance_ancestors(module_name, building_ancestors: building_ancestors) + ancestors.unshift(*module_ancestors.apply(module_args)) + end + end + end end def each_member_with_accessibility(members, accessibility: :public) members.each do |member| case member @@ -198,338 +220,383 @@ end end def build_instance(type_name) try_cache type_name, cache: instance_cache do - decl = env.find_class(type_name) - self_ancestor = Definition::Ancestor::Instance.new(name: type_name, - args: Types::Variable.build(decl.type_params.each.map(&:name))) - self_type = Types::ClassInstance.new(name: type_name, args: self_ancestor.args, location: nil) + entry = env.class_decls[type_name] - case decl - when AST::Declarations::Class, AST::Declarations::Module - ancestors = build_ancestors(self_ancestor) - definition_pairs = ancestors.map do |ancestor| + case entry + when Environment::ClassEntry, Environment::ModuleEntry + ancestors = instance_ancestors(type_name) + self_type = Types::ClassInstance.new(name: type_name, + args: Types::Variable.build(entry.type_params.each.map(&:name)), + location: nil) + + definition_pairs = ancestors.ancestors.map do |ancestor| case ancestor when Definition::Ancestor::Instance [ancestor, build_one_instance(ancestor.name)] when Definition::Ancestor::Singleton [ancestor, build_one_singleton(ancestor.name)] - when Definition::Ancestor::ExtensionInstance - [ancestor, build_one_instance(ancestor.name, extension_name: ancestor.extension_name)] - when Definition::Ancestor::ExtensionSingleton - [ancestor, build_one_extension_singleton(ancestor.name, extension_name: ancestor.extension_name)] + else + raise end end - if decl.is_a?(AST::Declarations::Module) - if decl.self_type - self_interface = absolute_type(decl.self_type, namespace: type_name.to_namespace) - ancestor = Definition::Ancestor::Instance.new(name: self_interface.name, - args: self_interface.args) - - definition_pairs.push [ancestor, build_one_instance(ancestor.name)] + if entry.is_a?(Environment::ModuleEntry) + if self_type_ancestor = module_self_ancestor(type_name, entry) + definition_pairs.push [ + self_type_ancestor, + build_interface(self_type_ancestor.name) + ] end end - merge_definitions(definition_pairs, decl: decl, self_type: self_type, ancestors: ancestors) + merge_definitions(type_name, definition_pairs, entry: entry, self_type: self_type, ancestors: ancestors) + + else + raise end end end + def module_self_ancestor(type_name, entry) + self_decls = entry.decls.select {|d| d.decl.self_type } + + return if self_decls.empty? + + selfs = self_decls.map do |d| + Definition::Ancestor::Instance.new( + name: d.decl.self_type.name, + args: d.decl.self_type.args + ) + end + + selfs.uniq! + + return selfs[0] if selfs.size == 1 + + raise ModuleSelfTypeMismatchError.new( + name: type_name, + entry: entry, + location: self_decls[0].decl.location + ) + end + def build_singleton(type_name) try_cache type_name, cache: singleton_cache do - decl = env.find_class(type_name) - self_ancestor = Definition::Ancestor::Singleton.new(name: type_name) - self_type = Types::ClassSingleton.new(name: type_name, location: nil) + entry = env.class_decls[type_name] - case decl - when AST::Declarations::Class, AST::Declarations::Module - ancestors = build_ancestors(self_ancestor) - definition_pairs = ancestors.map do |ancestor| + case entry + when Environment::ClassEntry, Environment::ModuleEntry + ancestors = singleton_ancestors(type_name) + self_type = Types::ClassSingleton.new(name: type_name, location: nil) + instance_type = Types::ClassInstance.new( + name: type_name, + args: Types::Variable.build(entry.type_params.each.map(&:name)), + location: nil + ) + + definition_pairs = ancestors.ancestors.map do |ancestor| case ancestor when Definition::Ancestor::Instance [ancestor, build_one_instance(ancestor.name)] when Definition::Ancestor::Singleton - [ancestor, build_one_singleton(ancestor.name)] - when Definition::Ancestor::ExtensionInstance - [ancestor, build_one_instance(ancestor.name, extension_name: ancestor.extension_name)] - when Definition::Ancestor::ExtensionSingleton - [ancestor, build_one_singleton(ancestor.name, extension_name: ancestor.extension_name)] + definition = build_one_singleton(ancestor.name) + definition = definition.sub(Substitution.build([], [], instance_type: instance_type)) + definition = definition.map_method_type do |method_type| + s = Substitution.build( + method_type.free_variables.to_a, + method_type.free_variables.map { Types::Bases::Any.new(location: nil) } + ) + method_type.sub(s) + end + + [ + ancestor, + definition + ] + else + raise end end - if decl.is_a?(AST::Declarations::Class) - definition_pairs.find {|ancestor, _| ancestor == self_ancestor }.tap do |_, definition| - unless definition.methods[:new]&.implemented_in == decl - instance_definition = build_instance(type_name) - class_params = decl.type_params.each.map(&:name) - initialize_method = instance_definition.methods[:initialize] - method_types = initialize_method.method_types.map do |method_type| - case method_type - when MethodType - fvs = method_type.free_variables + class_params - unless fvs.empty? - param_name_set = Set.new(class_params) - bound_variables = method_type.type_params - renamed_types = bound_variables.map do |x| - if param_name_set.member?(x) - Types::Variable.fresh(x) - else - Types::Variable.new(name: x, location: nil) - end - end - sub = Substitution.build(bound_variables, renamed_types) - method_type_params = renamed_types.unshift(*class_params) - else - sub = Substitution.build([], []) - method_type_params = method_type.type_params - end + merge_definitions(type_name, definition_pairs, entry: entry, self_type: self_type, ancestors: ancestors) + else + raise + end + end + end - MethodType.new( - type_params: method_type_params, - type: method_type.type.sub(sub).with_return_type(instance_definition.self_type), - block: method_type.block&.yield_self {|ty| ty.sub(sub) }, - location: method_type.location - ) - end - end.compact + def method_definition_members(type_name, entry, kind:) + interface_methods = {} + methods = {} - definition.methods[:new] = Definition::Method.new( - super_method: nil, - defined_in: nil, - implemented_in: env.find_class(RBS::BuiltinNames::Class.name), - method_types: method_types, - accessibility: :public, - attributes: [:incompatible], - annotations: [], - comment: nil + entry.decls.each do |d| + each_member_with_accessibility(d.decl.members) do |member, accessibility| + case member + when AST::Members::MethodDefinition + case kind + when :singleton + next unless member.singleton? + when :instance + next unless member.instance? + end + + methods[member.name] ||= [] + methods[member.name] << [ + member.update(types: member.types), + accessibility + ] + when AST::Members::Include, AST::Members::Extend + if member.name.interface? + if (kind == :instance && member.is_a?(AST::Members::Include)) || (kind == :singleton && member.is_a?(AST::Members::Extend)) + interface_name = member.name + interface_args = member.args + + interface_definition = build_interface(interface_name) + + InvalidTypeApplicationError.check!( + type_name: interface_name, + args: interface_args, + params: interface_definition.type_params_decl, + location: member.location ) + + sub = Substitution.build(interface_definition.type_params, interface_args) + + interface_definition.methods.each do |name, method| + interface_methods[name] = [method.sub(sub), member] + end end end end - - merge_definitions(definition_pairs, decl: decl, self_type: self_type, ancestors: ancestors) end end - end - def build_one_instance(type_name, extension_name: nil) - decl = if extension_name - env.each_extension(type_name).find {|ext| ext.extension_name == extension_name } or - raise "Unknown extension: #{type_name} (#{extension_name})" - else - env.find_class(type_name) - end + result = {} - case decl - when AST::Declarations::Interface - build_interface type_name, decl - else - namespace = type_name.to_namespace + interface_methods.each do |name, pair| + method_definition, _ = pair + result[name] = [:public, method_definition] + end - case decl - when AST::Declarations::Class, AST::Declarations::Module - self_type = Types::ClassInstance.new(name: type_name, - args: Types::Variable.build(decl.type_params.each.map(&:name)), - location: nil) - ancestors = [Definition::Ancestor::Instance.new(name: type_name, args: self_type.args)] - when AST::Declarations::Extension - self_type = Types::ClassInstance.new(name: type_name, args: Types::Variable.build(decl.type_params), location: nil) - ancestors = [Definition::Ancestor::ExtensionInstance.new(name: type_name, - extension_name: extension_name, - args: self_type.args)] + methods.each do |method_name, array| + if result[method_name] + unless array.all? {|pair| pair[0].overload? } + raise MethodDefinitionConflictWithInterfaceMixinError.new( + type_name: type_name, + method_name: method_name, + kind: :instance, + mixin_member: interface_methods[method_name][1], + entries: array.map(&:first) + ) + end + + unless array.all? {|pair| pair[1] == :public} + raise InconsistentMethodVisibilityError.new( + type_name: type_name, + method_name: method_name, + kind: :instance, + member_pairs: array + ) + end + + result[method_name] += array.map(&:first) + else + case + when array.size == 1 && !array[0][0].overload? + member, visibility = array[0] + result[method_name] = [visibility, nil, member] + + when array.count {|pair| !pair[0].overload? } == 1 + visibilities = array.group_by {|pair| pair[1] } + + if visibilities.size > 1 + raise InconsistentMethodVisibilityError.new( + type_name: type_name, + method_name: method_name, + kind: :instance, + member_pairs: array + ) + end + + overloads, primary = array.map(&:first).partition(&:overload?) + result[method_name] = [array[0][1], nil, *primary, *overloads] + + else + raise InvalidOverloadMethodError.new( + type_name: type_name, + method_name: method_name, + kind: :instance, + members: array.map(&:first) + ) + end end + end - Definition.new(declaration: decl, self_type: self_type, ancestors: ancestors).tap do |definition| - alias_members = [] + result + end - each_member_with_accessibility(decl.members) do |member, accessibility| - case member - when AST::Members::MethodDefinition - if member.instance? - name = member.name - method_types = member.types.map do |method_type| - case method_type - when MethodType - method_type.map_type do |type| - absolute_type(type, namespace: namespace) - end - when :super - :super - end - end + def build_one_instance(type_name) + try_cache(type_name, cache: one_instance_cache) do + entry = env.class_decls[type_name] - DuplicatedMethodDefinitionError.check!( - decl: decl, - methods: definition.methods, - name: name, - location: member.location - ) + self_type = Types::ClassInstance.new(name: type_name, + args: Types::Variable.build(entry.type_params.each.map(&:name)), + location: nil) + ancestors = [Definition::Ancestor::Instance.new(name: type_name, args: self_type.args)] - attrs = if name == :initialize - (member.attributes + [:incompatible]).uniq - else - member.attributes - end + Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: ancestors).tap do |definition| + method_definition_members(type_name, entry, kind: :instance).each do |method_name, array| + visibility, method_def, *members = array - definition.methods[name] = Definition::Method.new(super_method: nil, - method_types: method_types, - defined_in: decl, - implemented_in: decl, - accessibility: accessibility, - attributes: attrs, - annotations: member.annotations, - comment: member.comment) - end - when AST::Members::AttrReader, AST::Members::AttrAccessor, AST::Members::AttrWriter - name = member.name - type = absolute_type(member.type, namespace: namespace) - ivar_name = case member.ivar_name - when false - nil - else - member.ivar_name || :"@#{member.name}" - end + m = if method_def + Definition::Method.new( + super_method: nil, + accessibility: visibility, + defs: method_def.defs.map {|defn| defn.update(implemented_in: type_name) } + ) + else + Definition::Method.new( + super_method: nil, + accessibility: visibility, + defs: [] + ) + end - if member.is_a?(AST::Members::AttrReader) || member.is_a?(AST::Members::AttrAccessor) - definition.methods[name] = Definition::Method.new( - super_method: nil, - method_types: [ - MethodType.new( - type_params: [], - type: Types::Function.empty(type), - block: nil, - location: nil - ) - ], - defined_in: decl, - implemented_in: decl, - accessibility: accessibility, - attributes: [], - annotations: member.annotations, - comment: member.comment + definition.methods[method_name] = members.inject(m) do |original, member| + defs = member.types.map do |method_type| + Definition::Method::TypeDef.new( + type: method_type, + member: member, + implemented_in: type_name, + defined_in: type_name ) end - if member.is_a?(AST::Members::AttrWriter) || member.is_a?(AST::Members::AttrAccessor) - definition.methods[:"#{name}="] = Definition::Method.new( - super_method: nil, - method_types: [ - MethodType.new( - type_params: [], - type: Types::Function.new(required_positionals: [Types::Function::Param.new(name: name, type: type)], - optional_positionals: [], - rest_positionals: nil, - trailing_positionals: [], - required_keywords: {}, - optional_keywords: {}, - rest_keywords: nil, - return_type: type), - block: nil, - location: nil - ) - ], - defined_in: decl, - implemented_in: decl, - accessibility: accessibility, - attributes: [], - annotations: member.annotations, - comment: member.comment + Definition::Method.new( + super_method: nil, + defs: defs + original.defs, + accessibility: original.accessibility + ) + end + end + + entry.decls.each do |d| + each_member_with_accessibility(d.decl.members) do |member, accessibility| + case member + when AST::Members::AttrReader, AST::Members::AttrAccessor, AST::Members::AttrWriter + name = member.name + type = member.type + + ivar_name = case member.ivar_name + when false + nil + else + member.ivar_name || :"@#{member.name}" + end + + if member.is_a?(AST::Members::AttrReader) || member.is_a?(AST::Members::AttrAccessor) + definition.methods[name] = Definition::Method.new( + super_method: nil, + defs: [ + Definition::Method::TypeDef.new( + type: MethodType.new( + type_params: [], + type: Types::Function.empty(type), + block: nil, + location: nil + ), + member: member, + defined_in: type_name, + implemented_in: type_name + ) + ], + accessibility: accessibility + ) + end + + if member.is_a?(AST::Members::AttrWriter) || member.is_a?(AST::Members::AttrAccessor) + definition.methods[:"#{name}="] = Definition::Method.new( + super_method: nil, + defs: [ + Definition::Method::TypeDef.new( + type: MethodType.new( + type_params: [], + type: Types::Function.empty(type).update( + required_positionals: [Types::Function::Param.new(name: name, type: type)] + ), + block: nil, + location: nil + ), + member: member, + defined_in: type_name, + implemented_in: type_name + ), + ], + accessibility: accessibility + ) + end + + if ivar_name + definition.instance_variables[ivar_name] = Definition::Variable.new( + parent_variable: nil, + type: type, + declared_in: type_name + ) + end + + when AST::Members::InstanceVariable + definition.instance_variables[member.name] = Definition::Variable.new( + parent_variable: nil, + type: member.type, + declared_in: type_name ) - end - if ivar_name - definition.instance_variables[ivar_name] = Definition::Variable.new( + when AST::Members::ClassVariable + definition.class_variables[member.name] = Definition::Variable.new( parent_variable: nil, - type: type, - declared_in: decl + type: member.type, + declared_in: type_name ) - end - when AST::Members::Alias - if member.instance? - alias_members << member end - when AST::Members::Include - if member.name.interface? - absolute_name = absolute_type_name(member.name, namespace: namespace, location: member.location) - interface_definition = build_one_instance(absolute_name) - absolute_args = member.args.map {|ty| absolute_type(ty, namespace: namespace) } + end + end - InvalidTypeApplicationError.check!( - type_name: absolute_name, - args: absolute_args, - params: interface_definition.type_params_decl, - location: member.location - ) + entry.decls.each do |d| + d.decl.members.each do |member| + case member + when AST::Members::Alias + if member.instance? + UnknownMethodAliasError.check!( + methods: definition.methods, + original_name: member.old_name, + aliased_name: member.new_name, + location: member.location + ) - sub = Substitution.build(interface_definition.type_params, absolute_args) - interface_definition.methods.each do |name, method| - method_types = method.method_types.map do |method_type| - method_type.sub(sub).map_type do |type| - absolute_type(type, namespace: namespace) - end - end - DuplicatedMethodDefinitionError.check!( - decl: decl, + decl: d.decl, methods: definition.methods, - name: name, + name: member.new_name, location: member.location ) - definition.methods[name] = Definition::Method.new( - super_method: nil, - method_types: method_types, - defined_in: method.defined_in, - implemented_in: decl, - accessibility: method.accessibility, - attributes: [], - annotations: method.annotations, - comment: member.comment - ) + definition.methods[member.new_name] = definition.methods[member.old_name] end end - when AST::Members::InstanceVariable - definition.instance_variables[member.name] = Definition::Variable.new( - type: absolute_type(member.type, namespace: namespace), - parent_variable: nil, - declared_in: decl - ) - when AST::Members::ClassVariable - definition.class_variables[member.name] = Definition::Variable.new( - type: absolute_type(member.type, namespace: namespace), - parent_variable: nil, - declared_in: decl - ) end end - alias_members.each do |member| - UnknownMethodAliasError.check!( - methods: definition.methods, - original_name: member.old_name, - aliased_name: member.new_name, - location: member.location + entry.decls.each do |d| + validate_parameter_variance( + decl: d.decl, + methods: definition.methods ) - - DuplicatedMethodDefinitionError.check!( - decl: decl, - methods: definition.methods, - name: member.new_name, - location: member.location - ) - - # FIXME: may cause a problem if #old_name has super type - definition.methods[member.new_name] = definition.methods[member.old_name] end - - validate_parameter_variance( - decl: decl, - methods: definition.methods - ) end end end def validate_params_with(type_params, result:) @@ -541,28 +608,21 @@ end end end def validate_parameter_variance(decl:, methods:) - type_params = case decl - when AST::Declarations::Extension - env.find_class(decl.name.absolute!).type_params.rename_to(decl.type_params) - else - decl.type_params - end + type_params = decl.type_params - namespace = decl.name.absolute!.to_namespace calculator = VarianceCalculator.new(builder: self) param_names = type_params.each.map(&:name) errors = [] if decl.is_a?(AST::Declarations::Class) if decl.super_class - absolute_super_name = absolute_type_name(decl.super_class.name, namespace: namespace, location: decl.location) - absolute_args = decl.super_class.args.map {|type| absolute_type(type, namespace: namespace) } - result = calculator.in_inherit(name: absolute_super_name, args: absolute_args, variables: param_names) + super_class = decl.super_class + result = calculator.in_inherit(name: super_class.name, args: super_class.args, variables: param_names) validate_params_with type_params, result: result do |param| errors.push InvalidVarianceAnnotationError::InheritanceError.new( param: param ) @@ -572,13 +632,11 @@ decl.members.each do |member| case member when AST::Members::Include if member.name.class? - absolute_module_name = absolute_type_name(member.name, namespace: namespace, location: decl.location) - absolute_args = member.args.map {|type| absolute_type(type, namespace: namespace) } - result = calculator.in_inherit(name: absolute_module_name, args: absolute_args, variables: param_names) + result = calculator.in_inherit(name: member.name, args: member.args, variables: param_names) validate_params_with type_params, result: result do |param| errors.push InvalidVarianceAnnotationError::MixinError.new( include_member: member, param: param @@ -608,152 +666,155 @@ unless errors.empty? raise InvalidVarianceAnnotationError.new(decl: decl, errors: errors) end end - def build_one_singleton(type_name, extension_name: nil) - decl = if extension_name - env.each_extension(type_name).find {|ext| ext.extension_name == extension_name } or - raise "Unknown extension: #{type_name} (#{extension_name})" - else - env.find_class(type_name) - end + def build_one_singleton(type_name) + try_cache(type_name, cache: one_singleton_cache) do + entry = env.class_decls[type_name] - namespace = type_name.to_namespace - - case decl - when AST::Declarations::Module, AST::Declarations::Class self_type = Types::ClassSingleton.new(name: type_name, location: nil) ancestors = [Definition::Ancestor::Singleton.new(name: type_name)] - when AST::Declarations::Extension - self_type = Types::ClassSingleton.new(name: type_name, location: nil) - ancestors = [Definition::Ancestor::ExtensionSingleton.new(name: type_name, extension_name: extension_name)] - end - Definition.new(declaration: decl, self_type: self_type, ancestors: ancestors).tap do |definition| - alias_members = [] + Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: ancestors).tap do |definition| + method_definition_members(type_name, entry, kind: :singleton).each do |method_name, array| + visibility, method_def, *members = array - each_member_with_accessibility(decl.members) do |member, accessibility| - case member - when AST::Members::MethodDefinition - if member.singleton? - name = member.name - method_types = member.types.map do |method_type| - method_type.map_type do |type| - absolute_type(type, namespace: namespace) - end + m = Definition::Method.new( + super_method: nil, + defs: method_def&.yield_self do |method_def| + method_def.defs.map {|defn| defn.update(implemented_in: type_name) } + end || [], + accessibility: visibility + ) + definition.methods[method_name] = members.inject(m) do |original, new| + defs = new.types.map do |type| + Definition::Method::TypeDef.new( + type: type, + member: new, + defined_in: type_name, + implemented_in: type_name + ) end - - DuplicatedMethodDefinitionError.check!( - decl: decl, - methods: definition.methods, - name: name, - location: member.location + Definition::Method.new( + super_method: nil, + defs: defs + original.defs, + accessibility: original.accessibility ) + end + end - definition.methods[name] = Definition::Method.new(super_method: nil, - method_types: method_types, - defined_in: decl, - implemented_in: decl, - accessibility: accessibility, - attributes: member.attributes, - annotations: member.annotations, - comment: member.comment) + entry.decls.each do |d| + d.decl.members.each do |member| + case member + when AST::Members::Alias + if member.singleton? + UnknownMethodAliasError.check!( + methods: definition.methods, + original_name: member.old_name, + aliased_name: member.new_name, + location: member.location + ) + + DuplicatedMethodDefinitionError.check!( + decl: d.decl, + methods: definition.methods, + name: member.new_name, + location: member.location + ) + + definition.methods[member.new_name] = definition.methods[member.old_name] + end + end end - when AST::Members::Alias - if member.singleton? - alias_members << member - end - when AST::Members::Extend - if member.name.interface? - absolute_name = absolute_type_name(member.name, namespace: namespace, location: member.location) - interface_definition = build_one_instance(absolute_name) - absolute_args = member.args.map {|ty| absolute_type(ty, namespace: namespace) } + end - InvalidTypeApplicationError.check!( - type_name: absolute_name, - args: absolute_args, - params: interface_definition.type_params_decl, - location: member.location - ) + unless definition.methods.key?(:new) + instance = build_one_instance(type_name) + initialize = instance.methods[:initialize] - sub = Substitution.build(interface_definition.type_params, absolute_args) - interface_definition.methods.each do |name, method| - method_types = method.method_types.map do |method_type| - method_type.sub(sub).map_type do |type| - absolute_type(type, namespace: namespace) + if initialize + class_params = entry.type_params.each.map(&:name) + + initialize_defs = initialize.defs + definition.methods[:new] = Definition::Method.new( + super_method: nil, + defs: initialize_defs.map do |initialize_def| + method_type = initialize_def.type + + class_type_param_vars = Set.new(class_params) + method_type_param_vars = Set.new(method_type.type_params) + + if class_type_param_vars.intersect?(method_type_param_vars) + renamed_method_params = method_type.type_params.map do |name| + if class_type_param_vars.include?(name) + Types::Variable.fresh(name).name + else + name + end + end + method_params = class_params + renamed_method_params + + sub = Substitution.build(method_type.type_params, Types::Variable.build(renamed_method_params)) + else + method_params = class_params + method_type.type_params + sub = Substitution.build([], []) end - end - DuplicatedMethodDefinitionError.check!( - decl: decl, - methods: definition.methods, - name: name, - location: member.location + method_type = method_type.map_type {|ty| ty.sub(sub) } + method_type = method_type.update( + type_params: method_params, + type: method_type.type.with_return_type(Types::Bases::Instance.new(location: nil)) + ) + + Definition::Method::TypeDef.new( + type: method_type, + member: initialize_def.member, + defined_in: nil, + implemented_in: nil + ) + end, + accessibility: :public + ) + end + end + + entry.decls.each do |d| + each_member_with_accessibility(d.decl.members) do |member, _| + case member + when AST::Members::ClassInstanceVariable + definition.instance_variables[member.name] = Definition::Variable.new( + parent_variable: nil, + type: member.type, + declared_in: type_name ) - definition.methods[name] = Definition::Method.new( - super_method: nil, - method_types: method_types, - defined_in: method.defined_in, - implemented_in: decl, - accessibility: method.accessibility, - attributes: method.attributes, - annotations: method.annotations, - comment: member.comment + when AST::Members::ClassVariable + definition.class_variables[member.name] = Definition::Variable.new( + parent_variable: nil, + type: member.type, + declared_in: type_name ) end end - when AST::Members::ClassInstanceVariable - definition.instance_variables[member.name] = Definition::Variable.new( - type: absolute_type(member.type, namespace: namespace), - parent_variable: nil, - declared_in: decl - ) - when AST::Members::ClassVariable - definition.class_variables[member.name] = Definition::Variable.new( - type: absolute_type(member.type, namespace: namespace), - parent_variable: nil, - declared_in: decl - ) end end - - alias_members.each do |member| - UnknownMethodAliasError.check!( - methods: definition.methods, - original_name: member.old_name, - aliased_name: member.new_name, - location: member.location - ) - - DuplicatedMethodDefinitionError.check!( - decl: decl, - methods: definition.methods, - name: member.new_name, - location: member.location - ) - - # FIXME: may cause a problem if #old_name has super type - definition.methods[member.new_name] = definition.methods[member.old_name] - end end end - def merge_definitions(pairs, decl:, self_type:, ancestors:) - Definition.new(declaration: decl, self_type: self_type, ancestors: ancestors).tap do |definition| + def merge_definitions(type_name, pairs, entry:, self_type:, ancestors:) + Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: ancestors).tap do |definition| pairs.reverse_each do |(ancestor, current_definition)| sub = case ancestor - when Definition::Ancestor::Instance, Definition::Ancestor::ExtensionInstance + when Definition::Ancestor::Instance Substitution.build(current_definition.type_params, ancestor.args) - when Definition::Ancestor::Singleton, Definition::Ancestor::ExtensionSingleton + when Definition::Ancestor::Singleton Substitution.build([], []) end - namespace = current_definition.name.absolute!.to_namespace current_definition.methods.each do |name, method| - merge_method definition.methods, name, method, sub, namespace + merge_method definition.methods, name, method, sub end current_definition.instance_variables.each do |name, variable| merge_variable definition.instance_variables, name, variable end @@ -773,29 +834,17 @@ type: variable.type, declared_in: variable.declared_in ) end - def merge_method(methods, name, method, sub, namespace) + def merge_method(methods, name, method, sub) super_method = methods[name] methods[name] = Definition::Method.new( - method_types: method.method_types.flat_map do |method_type| - case method_type - when MethodType - [absolute_type(method_type.sub(sub), namespace: namespace)] - when :super - super_method.method_types - end - end, super_method: super_method, - defined_in: method.defined_in, - implemented_in: method.implemented_in, accessibility: method.accessibility, - attributes: method.attributes, - annotations: method.annotations, - comment: method.comment + defs: method.defs.map {|defn| defn.update(type: defn.type.sub(sub)) } ) end def try_cache(type_name, cache:) cached = cache[type_name] @@ -814,106 +863,101 @@ raise ex end end end - def build_interface(type_name, declaration = env.find_class(type_name)) - self_type = Types::Interface.new( - name: type_name, - args: declaration.type_params.each.map {|p| Types::Variable.new(name: p.name, location: nil) }, - location: nil - ) + def build_interface(type_name) + try_cache(type_name, cache: interface_cache) do + entry = env.interface_decls[type_name] + declaration = entry.decl - namespace = type_name.to_namespace + self_type = Types::Interface.new( + name: type_name, + args: Types::Variable.build(declaration.type_params.each.map(&:name)), + location: nil + ) - Definition.new(declaration: declaration, self_type: self_type, ancestors: []).tap do |definition| - alias_members = [] + Definition.new(type_name: type_name, entry: entry, self_type: self_type, ancestors: nil).tap do |definition| + include_members = [] + def_members = [] + alias_members = [] - declaration.members.each do |member| - case member - when AST::Members::Include - mixin_name = env.absolute_interface_name(member.name, namespace: namespace) || member.name.absolute! - mixin = build_one_instance(mixin_name) + declaration.members.each do |member| + case member + when AST::Members::Include + include_members << member + when AST::Members::MethodDefinition + def_members << member + when AST::Members::Alias + alias_members << member + end + end - args = member.args.map {|type| absolute_type(type, namespace: namespace) } - type_params = mixin.declaration.type_params + include_members.each do |member| + mixin = build_interface(member.name) + args = member.args + type_params = mixin.entry.decl.type_params + InvalidTypeApplicationError.check!( type_name: type_name, args: args, - params: type_params, + params: type_params.each.map(&:name), location: member.location ) sub = Substitution.build(type_params.each.map(&:name), args) mixin.methods.each do |name, method| definition.methods[name] = method.sub(sub) end end - end - declaration.members.each do |member| - case member - when AST::Members::MethodDefinition + def_members.each do |member| DuplicatedMethodDefinitionError.check!( decl: declaration, methods: definition.methods, name: member.name, location: member.location ) method = Definition::Method.new( super_method: nil, - method_types: member.types.map do |method_type| - method_type.map_type {|ty| absolute_type(ty, namespace: namespace) } + defs: member.types.map do |method_type| + Definition::Method::TypeDef.new( + type: method_type, + member: member, + defined_in: type_name, + implemented_in: nil + ) end, - defined_in: declaration, - implemented_in: nil, - accessibility: :public, - attributes: member.attributes, - annotations: member.annotations, - comment: member.comment + accessibility: :public ) definition.methods[member.name] = method - when AST::Members::Alias - alias_members << member end - end - alias_members.each do |member| - UnknownMethodAliasError.check!( - methods: definition.methods, - original_name: member.old_name, - aliased_name: member.new_name, - location: member.location - ) + alias_members.each do |member| + UnknownMethodAliasError.check!( + methods: definition.methods, + original_name: member.old_name, + aliased_name: member.new_name, + location: member.location + ) - DuplicatedMethodDefinitionError.check!( - decl: declaration, - methods: definition.methods, - name: member.new_name, - location: member.location - ) + DuplicatedMethodDefinitionError.check!( + decl: declaration, + methods: definition.methods, + name: member.new_name, + location: member.location + ) - # FIXME: may cause a problem if #old_name has super type - definition.methods[member.new_name] = definition.methods[member.old_name] + # FIXME: may cause a problem if #old_name has super type + definition.methods[member.new_name] = definition.methods[member.old_name] + end end end end - def absolute_type(type, namespace:) - env.absolute_type(type, namespace: namespace) do |type| - NoTypeFoundError.check!(type.name.absolute!, env: env, location: type.location) - end - end - - def absolute_type_name(type_name, namespace:, location:) - env.absolute_type_name(type_name, namespace: namespace) do |type_name| - NoTypeFoundError.check!(type_name.absolute!, env: env, location: location) - end - end - def expand_alias(type_name) - absolute_type(env.find_alias(type_name).type, namespace: type_name.namespace) + env.alias_decls[type_name].decl.type end end end