diff --git a/lib/typeprof/core/ast/sig_type.rb b/lib/typeprof/core/ast/sig_type.rb index c3b90632..57bbf76a 100644 --- a/lib/typeprof/core/ast/sig_type.rb +++ b/lib/typeprof/core/ast/sig_type.rb @@ -39,7 +39,7 @@ def self.typecheck_for_prepended_modules(genv, changes, a_ty, f_mod, f_args, sub if prep_decl.is_a?(AST::SigPrependNode) && prep_mod.type_params prep_ty = genv.get_instance_type(prep_mod, prep_decl.args, changes, {}, a_ty) else - type_params = prep_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support + type_params = prep_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support prep_ty = Type::Instance.new(genv, prep_mod, type_params) end if prep_ty.mod == f_mod @@ -64,7 +64,7 @@ def self.typecheck_for_included_modules(genv, changes, a_ty, f_mod, f_args, subs if inc_decl.is_a?(AST::SigIncludeNode) && inc_mod.type_params inc_ty = genv.get_instance_type(inc_mod, inc_decl.args, changes, {}, a_ty) else - type_params = inc_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support + type_params = inc_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support inc_ty = Type::Instance.new(genv, inc_mod, type_params) end if inc_ty.mod == f_mod @@ -89,7 +89,7 @@ def self.typecheck_for_extended_modules(genv, changes, a_ty, f_mod, f_args, subs if ext_decl.is_a?(AST::SigExtendNode) && ext_mod.type_params ext_ty = genv.get_instance_type(ext_mod, ext_decl.args, changes, {}, a_ty) else - type_params = ext_mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support + type_params = ext_mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support ext_ty = Type::Instance.new(genv, ext_mod, type_params) end if ext_ty.mod == f_mod @@ -705,6 +705,10 @@ def covariant_vertex0(genv, changes, vtx, subst) return unless cpath mod = genv.resolve_cpath(cpath) args = @args.map {|arg| arg.covariant_vertex(genv, changes, subst) } + # Omitted type arguments (e.g., `Foo` for `Foo[T]`) default to untyped + mod.type_params.drop(args.size).each do |_, default_ty| + args << (default_ty ? default_ty.covariant_vertex(genv, changes, subst) : genv.untyped_arg) + end changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx) end @@ -716,7 +720,7 @@ def contravariant_vertex0(genv, changes, vtx, subst) # TODO: report error for wrong type arguments # TODO: support default type args args = mod.type_params.zip(@args).map do |_, arg| - arg ? arg.contravariant_vertex(genv, changes, subst) : Source.new + arg ? arg.contravariant_vertex(genv, changes, subst) : genv.untyped_arg end changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx) end @@ -1095,6 +1099,10 @@ def covariant_vertex0(genv, changes, vtx, subst) return unless cpath mod = genv.resolve_cpath(cpath) args = @args.map {|arg| arg.covariant_vertex(genv, changes, subst) } + # Omitted type arguments (e.g., `Foo` for `Foo[T]`) default to untyped + mod.type_params.drop(args.size).each do |_, default_ty| + args << (default_ty ? default_ty.covariant_vertex(genv, changes, subst) : genv.untyped_arg) + end changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx) end @@ -1104,6 +1112,7 @@ def contravariant_vertex0(genv, changes, vtx, subst) return unless cpath mod = genv.resolve_cpath(cpath) args = @args.map {|arg| arg.contravariant_vertex(genv, changes, subst) } + mod.type_params.drop(args.size).each { args << genv.untyped_arg } changes.add_edge(genv, Source.new(Type::Instance.new(genv, mod, args)), vtx) end diff --git a/lib/typeprof/core/env.rb b/lib/typeprof/core/env.rb index d1b8ce83..68c31038 100644 --- a/lib/typeprof/core/env.rb +++ b/lib/typeprof/core/env.rb @@ -42,6 +42,11 @@ def initialize @bot_type = Type::Bot.new(self) + # The vertex for a type argument that is not given. This must be shared: + # a fresh vertex makes a different Instance type and a different edge on + # every run, so a box that uses it is re-run forever. + @untyped_arg = Source.new + @run_count = 0 end @@ -52,7 +57,7 @@ def initialize attr_reader :obj_type, :nil_type, :true_type, :false_type, :str_type attr_reader :int_type, :float_type, :rational_type, :complex_type attr_reader :proc_type, :symbol_type, :method_type, :set_type, :regexp_type - attr_reader :bot_type + attr_reader :bot_type, :untyped_arg def gen_ary_type(elem_vtx) Type::Instance.new(self, @mod_ary, [elem_vtx]) @@ -130,18 +135,18 @@ def get_instance_type(mod, type_args, changes, base_ty_env, base_ty) ty_env = base_ty_env.dup if base_ty.is_a?(Type::Instance) base_ty.mod.type_params.zip(base_ty.args) do |(param, default_ty), arg| - ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new) + ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : @untyped_arg) end elsif base_ty.is_a?(Type::Singleton) base_ty.mod.type_params&.each do |(param, default_ty)| - ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : Source.new + ty_env[param] = default_ty ? default_ty.covariant_vertex(self, changes, ty_env) : @untyped_arg end end args = mod.type_params.zip(type_args).map do |(param, default_ty), arg| if changes - (arg || default_ty)&.covariant_vertex(self, changes, ty_env) || Source.new + (arg || default_ty)&.covariant_vertex(self, changes, ty_env) || @untyped_arg else - Source.new + @untyped_arg end end Type::Instance.new(self, mod, args) @@ -463,7 +468,7 @@ def get_self(genv) case @scope_level when :instance mod = genv.resolve_cpath(@cpath || []) - type_params = mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support + type_params = mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support ty = Type::Instance.new(genv, mod, type_params) Source.new(ty) when :class diff --git a/lib/typeprof/core/graph/box.rb b/lib/typeprof/core/graph/box.rb index 9883abc7..51ab2749 100644 --- a/lib/typeprof/core/graph/box.rb +++ b/lib/typeprof/core/graph/box.rb @@ -779,7 +779,7 @@ def run0(genv, changes) ty = Type::Singleton.new(genv, mod) param_map0 = Type.default_param_map(genv, ty) else - type_params = mod.type_params.map {|(_name, _default_ty)| Source.new() } # TODO: better support + type_params = mod.type_params.map {|(_name, _default_ty)| genv.untyped_arg } # TODO: better support ty = Type::Instance.new(genv, mod, type_params) param_map0 = Type.default_param_map(genv, ty) if ty.is_a?(Type::Instance) @@ -984,7 +984,7 @@ def run0(genv, changes) ty_env = Type.default_param_map(genv, orig_ty) if ty.is_a?(Type::Instance) ty.mod.type_params.zip(ty.args) do |(param, default_ty), arg| - ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(genv, changes, ty_env) : Source.new) + ty_env[param] = arg || (default_ty ? default_ty.covariant_vertex(genv, changes, ty_env) : genv.untyped_arg) end end mdecl.resolve_overloads(changes, genv, @node, ty_env, a_args, @ret) do |method_type| @@ -1095,7 +1095,7 @@ def resolve_prepended_modules(genv, changes, base_ty_env, ty, mid, &blk) if prep_decl.is_a?(AST::SigPrependNode) && prep_mod.type_params prep_ty = genv.get_instance_type(prep_mod, prep_decl.args, changes, base_ty_env, ty) else - type_params = prep_mod.type_params.map { Source.new() } # TODO: better support + type_params = prep_mod.type_params.map { genv.untyped_arg } # TODO: better support prep_ty = Type::Instance.new(genv, prep_mod, type_params) end @@ -1150,7 +1150,7 @@ def resolve_included_modules(genv, changes, base_ty_env, ty, mid, &blk) if inc_decl.is_a?(AST::SigIncludeNode) && inc_mod.type_params inc_ty = genv.get_instance_type(inc_mod, inc_decl.args, changes, base_ty_env, ty) else - type_params = inc_mod.type_params.map { Source.new() } # TODO: better support + type_params = inc_mod.type_params.map { genv.untyped_arg } # TODO: better support inc_ty = Type::Instance.new(genv, inc_mod, type_params) end @@ -1181,7 +1181,7 @@ def resolve_extended_modules(genv, changes, base_ty_env, ty, mid, &blk) if ext_decl.is_a?(AST::SigExtendNode) && ext_mod.type_params ext_ty = genv.get_instance_type(ext_mod, ext_decl.args, changes, base_ty_env, ty) else - type_params = ext_mod.type_params.map { Source.new() } # TODO: better support + type_params = ext_mod.type_params.map { genv.untyped_arg } # TODO: better support ext_ty = Type::Instance.new(genv, ext_mod, type_params) end diff --git a/lib/typeprof/core/type.rb b/lib/typeprof/core/type.rb index 7ddf8634..512fe38e 100644 --- a/lib/typeprof/core/type.rb +++ b/lib/typeprof/core/type.rb @@ -77,7 +77,7 @@ def show def get_instance_type(genv) params = @mod.type_params - Instance.new(genv, @mod, params ? params.map { Source.new } : []) # TODO: respect param_default_types + Instance.new(genv, @mod, params ? params.map { genv.untyped_arg } : []) # TODO: respect param_default_types end end diff --git a/scenario/rbs/generic-superclass-without-args.rb b/scenario/rbs/generic-superclass-without-args.rb new file mode 100644 index 00000000..19314f9c --- /dev/null +++ b/scenario/rbs/generic-superclass-without-args.rb @@ -0,0 +1,23 @@ +## update: test.rbs +class Gen[T] + def self.create: () -> instance +end + +class Object + def accept: (Gen[Integer]) -> String +end + +## update: test.rb +class Sub < Gen +end + +def test + accept(Sub.create) +end + +## assert +class Sub < Gen +end +class Object + def test: -> String +end diff --git a/scenario/rbs/omitted-type-args.rb b/scenario/rbs/omitted-type-args.rb new file mode 100644 index 00000000..bcd278e9 --- /dev/null +++ b/scenario/rbs/omitted-type-args.rb @@ -0,0 +1,40 @@ +## update: test.rbs +class Gen[T] +end + +class D[T, U = String] + def u: () -> U +end + +interface _I[T] + def get: () -> T +end + +class Object + def gen: () -> Gen + def d: () -> D + def i: () -> _I + def accept_gen: (Gen[Integer]) -> String + def accept_d: (D[Integer, String]) -> String + def accept_i: (_I[Integer]) -> String +end + +## update: test.rb +def test_gen = accept_gen(gen) +def test_d = accept_d(d) +def test_i = accept_i(i) +def raw_gen = gen +def raw_d = d +def raw_d_u = d.u +def raw_i = i + +## assert +class Object + def test_gen: -> String + def test_d: -> String + def test_i: -> String + def raw_gen: -> Gen[untyped] + def raw_d: -> D[untyped, String] + def raw_d_u: -> String + def raw_i: -> _I[untyped] +end