From 0951fdbdc3b224211882ad41c0d2fa45255e4ec8 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 20 Jul 2022 10:12:13 +0900 Subject: [PATCH 1/3] Fix Constraints#eliminate_variable --- lib/steep/subtyping/constraints.rb | 12 ++++++++++++ sig/steep/subtyping/constraints.rbs | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/steep/subtyping/constraints.rb b/lib/steep/subtyping/constraints.rb index 7c32b343d..ed7632bab 100644 --- a/lib/steep/subtyping/constraints.rb +++ b/lib/steep/subtyping/constraints.rb @@ -169,6 +169,18 @@ def eliminate_variable(type, to:) else type end + when AST::Types::Tuple + AST::Types::Tuple.new( + types: type.types.map {|ty| eliminate_variable(ty, to: AST::Builtin.any_type) }, + location: type.location + ) + when AST::Types::Record + AST::Types::Record.new( + elements: type.elements.transform_values {|ty| eliminate_variable(ty, to: AST::Builtin.any_type) }, + location: type.location + ) + when AST::Types::Proc + type.map_type {|ty| eliminate_variable(ty, to: AST::Builtin.any_type) } else type end diff --git a/sig/steep/subtyping/constraints.rbs b/sig/steep/subtyping/constraints.rbs index 26abf5112..9429268a3 100644 --- a/sig/steep/subtyping/constraints.rbs +++ b/sig/steep/subtyping/constraints.rbs @@ -65,7 +65,7 @@ module Steep def add: (untyped var, ?sub_type: untyped?, ?super_type: untyped?, ?skip: bool) -> untyped - def eliminate_variable: (untyped `type`, to: untyped) -> untyped + def eliminate_variable: (AST::Types::t `type`, to: AST::Types::t) -> AST::Types::t def unknown?: (untyped var) -> untyped From 4ac3c8ad758b16bb4e95a837d239c675ba75cbdc Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 20 Jul 2022 10:12:24 +0900 Subject: [PATCH 2/3] Add Types::Proc type --- sig/steep/ast/types/proc.rbs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/sig/steep/ast/types/proc.rbs b/sig/steep/ast/types/proc.rbs index f0b3f2515..183511fa7 100644 --- a/sig/steep/ast/types/proc.rbs +++ b/sig/steep/ast/types/proc.rbs @@ -4,41 +4,42 @@ module Steep class Proc attr_reader location: untyped - attr_reader type: untyped + attr_reader type: Interface::Function - attr_reader block: untyped + attr_reader block: Interface::Block? - def initialize: (type: untyped, block: untyped, ?location: untyped) -> void + def initialize: (type: Interface::Function, block: Interface::Block?, ?location: untyped) -> void - def ==: (untyped other) -> untyped + def ==: (untyped other) -> bool - def hash: () -> untyped + def hash: () -> Integer alias eql? == - def subst: (untyped s) -> untyped + def subst: (Interface::Substitution s) -> Proc def to_s: () -> ::String - def free_variables: () -> untyped + def free_variables: () -> Set[Symbol] include Helper::ChildrenLevel - def level: () -> untyped + def level: () -> Array[Integer] - def closed?: () -> untyped + def closed?: () -> bool - def with_location: (untyped new_location) -> untyped + def with_location: (untyped new_location) -> Proc - def map_type: () ?{ () -> untyped } -> untyped + def map_type: () { (AST::Types::t) -> AST::Types::t } -> Proc - def one_arg?: () -> untyped + def one_arg?: () -> bool - def back_type: () -> untyped + def back_type: () -> AST::Types::t - def block_required?: () -> untyped + def block_required?: () -> bool - def each_child: () ?{ () -> untyped } -> untyped + def each_child: () { (AST::Types::t) -> void } -> void + | () -> Enumerator[AST::Types::t, void] end end end From 7b4cfe268533a293a12b17c3f2cd813957088a16 Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 20 Jul 2022 10:12:55 +0900 Subject: [PATCH 3/3] Ignore type variables in hint for lambda --- lib/steep/type_construction.rb | 4 ++++ sig/steep/type_construction.rbs | 4 ++-- test/type_construction_test.rb | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/steep/type_construction.rb b/lib/steep/type_construction.rb index a0fe4591b..5c2e60fcb 100644 --- a/lib/steep/type_construction.rb +++ b/lib/steep/type_construction.rb @@ -2731,6 +2731,10 @@ def type_lambda(node, params_node:, body_node:, type_hint:) ) if expected_block_type = block_constr.block_context.body_type + type_vars = expected_block_type.free_variables + subst = Interface::Substitution.build(type_vars, type_vars.map { AST::Builtin.any_type }) + expected_block_type = expected_block_type.subst(subst) + check_relation(sub_type: return_type, super_type: expected_block_type).else do |result| block_constr.typing.add_error( Diagnostic::Ruby::BlockBodyTypeMismatch.new( diff --git a/sig/steep/type_construction.rbs b/sig/steep/type_construction.rbs index 96f858b90..aa9a3a78a 100644 --- a/sig/steep/type_construction.rbs +++ b/sig/steep/type_construction.rbs @@ -98,7 +98,7 @@ module Steep def synthesize: (Parser::AST::Node node, ?hint: AST::Types::t?, ?condition: bool) -> Pair - def check: (Parser::AST::Node node, untyped `type`, ?constraints: untyped) { (untyped, untyped, untyped) -> untyped } -> bool + def check: (Parser::AST::Node node, AST::Types::t `type`, ?constraints: Subtyping::Constraints) { (AST::Types::t, AST::Types::t, Subtyping::Result::Base) -> void } -> Pair def masgn_lhs?: (untyped lhs) -> untyped @@ -169,7 +169,7 @@ module Steep def try_method_type: (Parser::AST::Node node, receiver_type: AST::Types::t, method_name: Symbol, method_type: Interface::MethodType, arguments: Array[Parser::AST::Node], block_params: Parser::AST::Node?, block_body: Parser::AST::Node?, topdown_hint: untyped) -> [TypeInference::MethodCall::t, TypeConstruction] - def type_check_argument: (untyped node, receiver_type: untyped, type: untyped, constraints: untyped, errors: untyped, ?report_node: untyped) -> untyped + def type_check_argument: (Parser::AST::Node node, receiver_type: AST::Types::t, type: AST::Types::t, constraints: Subtyping::Constraints, errors: Array[Diagnostic::Ruby::Base], ?report_node: Parser::AST::Node) -> Pair def type_block_without_hint: (node: untyped, block_annotations: untyped, block_params: untyped, block_body: untyped) { () -> untyped } -> untyped diff --git a/test/type_construction_test.rb b/test/type_construction_test.rb index d46d9ece2..d26184c52 100644 --- a/test/type_construction_test.rb +++ b/test/type_construction_test.rb @@ -9616,4 +9616,24 @@ def test_assignment_in_block end end end + + def test_issue_595_generics_type_inference + with_checker(<<-RBS) do |checker| +class ConfigurationReader + def read: [T](T, ^() -> T) -> T +end + RBS + source = parse_ruby(<<-RUBY) +reader = ConfigurationReader.new +reader.read("123", -> () { 123 }) + RUBY + + with_standard_construction(checker, source) do |construction, typing| + type, _, context = construction.synthesize(source.node) + + assert_no_error typing + assert_equal parse_type("::Integer | ::String"), type + end + end + end end