Class: Steep::TypeInference::CaseWhen
Defined Under Namespace
Classes: WhenPatterns
Instance Attribute Summary collapse
-
#assignment_node ⇒ Object
readonly
Returns the value of attribute assignment_node.
-
#clause_results ⇒ Object
readonly
Returns the value of attribute clause_results.
-
#condition_node ⇒ Object
readonly
Returns the value of attribute condition_node.
-
#else_node ⇒ Object
readonly
Returns the value of attribute else_node.
-
#else_result ⇒ Object
readonly
Returns the value of attribute else_result.
-
#initial_constr ⇒ Object
readonly
Returns the value of attribute initial_constr.
-
#location ⇒ Object
readonly
Returns the value of attribute location.
-
#logic ⇒ Object
readonly
Returns the value of attribute logic.
-
#node ⇒ Object
readonly
Returns the value of attribute node.
-
#value_node ⇒ Object
readonly
Returns the value of attribute value_node.
-
#var_name ⇒ Object
readonly
Returns the value of attribute var_name.
-
#when_nodes ⇒ Object
readonly
Returns the value of attribute when_nodes.
Class Method Summary collapse
Instance Method Summary collapse
- #else_clause ⇒ Object
- #has_else_clause? ⇒ Boolean
-
#initialize(node, logic) ⇒ CaseWhen
constructor
A new instance of CaseWhen.
- #latest_result ⇒ Object
- #propagate_value_node_type(env) ⇒ Object
- #result ⇒ Object
- #rewrite_condition_node(var_name, node) ⇒ Object
- #typing ⇒ Object
- #when_clauses ⇒ Object
Methods included from NodeHelper
clone_node, deconstruct_case_node, deconstruct_case_node!, deconstruct_if_node, deconstruct_if_node!, deconstruct_resbody_node, deconstruct_resbody_node!, deconstruct_rescue_node, deconstruct_rescue_node!, deconstruct_send_node, deconstruct_send_node!, deconstruct_sendish_and_block_nodes, deconstruct_when_node, deconstruct_when_node!, deconstruct_whileish_node, deconstruct_whileish_node!, each_child_node, each_descendant_node, private_send?, test_case_node, test_if_node, test_resbody_node, test_rescue_node, test_send_node, test_when_node, test_whileish_node, value_node?
Constructor Details
#initialize(node, logic) ⇒ CaseWhen
Returns a new instance of CaseWhen.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/steep/type_inference/case_when.rb', line 119 def initialize(node, logic) @node = node condition_node, when_nodes, else_node, location = deconstruct_case_node!(node) condition_node or raise "CaseWhen works for case-when syntax with condition node" @condition_node = condition_node @when_nodes = when_nodes @else_node = else_node @location = location @logic = logic @clause_results = [] type, constr = yield(condition_node) @var_name = "__case_when:#{SecureRandom.alphanumeric(5)}__".to_sym @value_node, @assignment_node = rewrite_condition_node(var_name, condition_node) @initial_constr = constr.update_type_env do |env| env.merge(local_variable_types: { var_name => [type, nil] }) end end |
Instance Attribute Details
#assignment_node ⇒ Object (readonly)
Returns the value of attribute assignment_node.
117 118 119 |
# File 'lib/steep/type_inference/case_when.rb', line 117 def assignment_node @assignment_node end |
#clause_results ⇒ Object (readonly)
Returns the value of attribute clause_results.
116 117 118 |
# File 'lib/steep/type_inference/case_when.rb', line 116 def clause_results @clause_results end |
#condition_node ⇒ Object (readonly)
Returns the value of attribute condition_node.
115 116 117 |
# File 'lib/steep/type_inference/case_when.rb', line 115 def condition_node @condition_node end |
#else_node ⇒ Object (readonly)
Returns the value of attribute else_node.
115 116 117 |
# File 'lib/steep/type_inference/case_when.rb', line 115 def else_node @else_node end |
#else_result ⇒ Object (readonly)
Returns the value of attribute else_result.
116 117 118 |
# File 'lib/steep/type_inference/case_when.rb', line 116 def else_result @else_result end |
#initial_constr ⇒ Object (readonly)
Returns the value of attribute initial_constr.
116 117 118 |
# File 'lib/steep/type_inference/case_when.rb', line 116 def initial_constr @initial_constr end |
#location ⇒ Object (readonly)
Returns the value of attribute location.
115 116 117 |
# File 'lib/steep/type_inference/case_when.rb', line 115 def location @location end |
#logic ⇒ Object (readonly)
Returns the value of attribute logic.
116 117 118 |
# File 'lib/steep/type_inference/case_when.rb', line 116 def logic @logic end |
#node ⇒ Object (readonly)
Returns the value of attribute node.
115 116 117 |
# File 'lib/steep/type_inference/case_when.rb', line 115 def node @node end |
#value_node ⇒ Object (readonly)
Returns the value of attribute value_node.
117 118 119 |
# File 'lib/steep/type_inference/case_when.rb', line 117 def value_node @value_node end |
#var_name ⇒ Object (readonly)
Returns the value of attribute var_name.
117 118 119 |
# File 'lib/steep/type_inference/case_when.rb', line 117 def var_name @var_name end |
#when_nodes ⇒ Object (readonly)
Returns the value of attribute when_nodes.
115 116 117 |
# File 'lib/steep/type_inference/case_when.rb', line 115 def when_nodes @when_nodes end |
Class Method Details
.type_check(constr, node, logic, hint:, condition:) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/steep/type_inference/case_when.rb', line 71 def self.type_check(constr, node, logic, hint:, condition:) case_when = new(node, logic) do |condition_node| constr.synthesize(condition_node) end case_when.when_clauses() do |when_pats, patterns, body_node, loc| patterns.each do |pat| when_pats.add_pattern(pat) {|test, constr| constr.synthesize(test) } end body_constr, body_unreachable = when_pats.body_result() do |env| case_when.propagate_value_node_type(env) end if body_node body_constr = body_constr.for_branch(body_node) type, body_constr = body_constr.synthesize(body_node, hint: hint, condition: condition) else type = AST::Builtin.nil_type end body_result = LogicTypeInterpreter::Result.new( type: type, env: body_constr.context.type_env, unreachable: body_unreachable ) falsy_constr, falsy_unreachable = when_pats.falsy_result next_result = LogicTypeInterpreter::Result.new( type: AST::Builtin.any_type, # Unused for falsy pattern env: falsy_constr.context.type_env, unreachable: falsy_unreachable ) [body_result, next_result] end case_when.else_clause do |else_node, constr| constr.synthesize(else_node, hint: hint, condition: condition) end case_when.result() end |
Instance Method Details
#else_clause ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/steep/type_inference/case_when.rb', line 178 def else_clause() unless else_loc = has_else_clause? return end constr, unreachable = latest_result constr = constr.update_type_env do |env| propagate_value_node_type(env) || env end @else_result = if else_node yield(else_node, constr) else TypeConstruction::Pair.new(type: AST::Builtin.nil_type, constr: constr) end else_result or raise if unreachable if else_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: else_result.type, super_type: AST::Builtin.bottom_type) typing.add_error( Diagnostic::Ruby::UnreachableValueBranch.new( node: else_node || node, type: else_result.type, location: else_loc ) ) end end end |
#has_else_clause? ⇒ Boolean
257 258 259 |
# File 'lib/steep/type_inference/case_when.rb', line 257 def has_else_clause? location.else end |
#latest_result ⇒ Object
211 212 213 214 215 216 217 218 219 220 |
# File 'lib/steep/type_inference/case_when.rb', line 211 def latest_result if (_, falsy_result = clause_results.last) [ initial_constr.update_type_env { falsy_result.env }, falsy_result.unreachable ] else [initial_constr, false] end end |
#propagate_value_node_type(env) ⇒ Object
292 293 294 295 296 297 298 299 300 |
# File 'lib/steep/type_inference/case_when.rb', line 292 def propagate_value_node_type(env) if value_node if (call = initial_constr.typing.method_calls[value_node]).is_a?(MethodCall::Typed) if env[value_node] env.merge(pure_method_calls: { value_node => [call, env[var_name]] }) end end end end |
#result ⇒ Object
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/steep/type_inference/case_when.rb', line 222 def result results = clause_results.filter_map do |body, _| unless body.unreachable body end end next_constr, next_clause_unreachable = latest_result unless next_clause_unreachable if else_result results << LogicTypeInterpreter::Result.new( type: else_result.type, env: else_result.context.type_env, unreachable: false # Unused ) else results << LogicTypeInterpreter::Result.new( type: AST::Builtin.nil_type, env: next_constr.context.type_env, unreachable: false ) end end results.reject! { _1.type.is_a?(AST::Types::Bot) } types = results.map {|result| result.type } envs = results.map {|result| result.env } [ types, envs ] end |
#rewrite_condition_node(var_name, node) ⇒ Object
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/steep/type_inference/case_when.rb', line 265 def rewrite_condition_node(var_name, node) case node.type when :lvasgn name, rhs = node.children value, rhs = rewrite_condition_node(var_name, rhs) [value, node.updated(nil, [name, rhs])] when :lvar name, = node.children [ nil, node.updated(:lvasgn, [name, node.updated(:lvar, [var_name])]) ] when :begin *children, last = node.children value_node, last = rewrite_condition_node(var_name, last) [ value_node, node.updated(nil, children.push(last)) ] else [ node, node.updated(:lvar, [var_name]) ] end end |
#typing ⇒ Object
261 262 263 |
# File 'lib/steep/type_inference/case_when.rb', line 261 def typing logic.typing end |
#when_clauses ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/steep/type_inference/case_when.rb', line 142 def when_clauses() when_nodes.each do |when_node| clause_constr, unreachable = latest_result patterns, body, loc = deconstruct_when_node!(when_node) when_pats = WhenPatterns.new( logic, clause_constr, unreachable, assignment_node ) body_result, next_result = yield( when_pats, patterns, body, loc ) if body_result.unreachable if body_result.type.is_a?(AST::Types::Any) || initial_constr.no_subtyping?(sub_type: body_result.type, super_type: AST::Builtin.bottom_type) typing.add_error( Diagnostic::Ruby::UnreachableValueBranch.new( node: when_node, type: body_result.type, location: loc.keyword ) ) end end clause_results << [body_result, next_result] end end |