houndstooth 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +11 -0
  6. data/Gemfile.lock +49 -0
  7. data/README.md +99 -0
  8. data/bin/houndstooth.rb +183 -0
  9. data/fuzz/cases/x.rb +8 -0
  10. data/fuzz/cases/y.rb +8 -0
  11. data/fuzz/cases/z.rb +22 -0
  12. data/fuzz/ruby.dict +64 -0
  13. data/fuzz/run +21 -0
  14. data/lib/houndstooth/environment/builder.rb +260 -0
  15. data/lib/houndstooth/environment/type_parser.rb +149 -0
  16. data/lib/houndstooth/environment/types/basic/type.rb +85 -0
  17. data/lib/houndstooth/environment/types/basic/type_instance.rb +54 -0
  18. data/lib/houndstooth/environment/types/compound/union_type.rb +72 -0
  19. data/lib/houndstooth/environment/types/defined/base_defined_type.rb +23 -0
  20. data/lib/houndstooth/environment/types/defined/defined_type.rb +137 -0
  21. data/lib/houndstooth/environment/types/defined/pending_defined_type.rb +14 -0
  22. data/lib/houndstooth/environment/types/method/method.rb +79 -0
  23. data/lib/houndstooth/environment/types/method/method_type.rb +144 -0
  24. data/lib/houndstooth/environment/types/method/parameters.rb +53 -0
  25. data/lib/houndstooth/environment/types/method/special_constructor_method.rb +15 -0
  26. data/lib/houndstooth/environment/types/special/instance_type.rb +9 -0
  27. data/lib/houndstooth/environment/types/special/self_type.rb +9 -0
  28. data/lib/houndstooth/environment/types/special/type_parameter_placeholder.rb +38 -0
  29. data/lib/houndstooth/environment/types/special/untyped_type.rb +11 -0
  30. data/lib/houndstooth/environment/types/special/void_type.rb +12 -0
  31. data/lib/houndstooth/environment/types.rb +3 -0
  32. data/lib/houndstooth/environment.rb +74 -0
  33. data/lib/houndstooth/errors.rb +53 -0
  34. data/lib/houndstooth/instructions.rb +698 -0
  35. data/lib/houndstooth/interpreter/const_internal.rb +148 -0
  36. data/lib/houndstooth/interpreter/objects.rb +142 -0
  37. data/lib/houndstooth/interpreter/runtime.rb +309 -0
  38. data/lib/houndstooth/interpreter.rb +7 -0
  39. data/lib/houndstooth/semantic_node/control_flow.rb +218 -0
  40. data/lib/houndstooth/semantic_node/definitions.rb +253 -0
  41. data/lib/houndstooth/semantic_node/identifiers.rb +308 -0
  42. data/lib/houndstooth/semantic_node/keywords.rb +45 -0
  43. data/lib/houndstooth/semantic_node/literals.rb +226 -0
  44. data/lib/houndstooth/semantic_node/operators.rb +126 -0
  45. data/lib/houndstooth/semantic_node/parameters.rb +108 -0
  46. data/lib/houndstooth/semantic_node/send.rb +349 -0
  47. data/lib/houndstooth/semantic_node/super.rb +12 -0
  48. data/lib/houndstooth/semantic_node.rb +119 -0
  49. data/lib/houndstooth/stdlib.rb +6 -0
  50. data/lib/houndstooth/type_checker.rb +462 -0
  51. data/lib/houndstooth.rb +53 -0
  52. data/spec/ast_to_node_spec.rb +889 -0
  53. data/spec/environment_spec.rb +323 -0
  54. data/spec/instructions_spec.rb +291 -0
  55. data/spec/integration_spec.rb +785 -0
  56. data/spec/interpreter_spec.rb +170 -0
  57. data/spec/self_spec.rb +7 -0
  58. data/spec/spec_helper.rb +50 -0
  59. data/test/ruby_interpreter_test.rb +162 -0
  60. data/types/stdlib.htt +170 -0
  61. metadata +110 -0
@@ -0,0 +1,108 @@
1
+ module Houndstooth::SemanticNode
2
+ # A set of parameters accepted by a method definition or block.
3
+ class Parameters < Base
4
+ register_ast_converter :args do |ast_node|
5
+ parameters = Parameters.new(
6
+ ast_node: ast_node,
7
+ positional_parameters: [],
8
+ optional_parameters: [],
9
+ keyword_parameters: [],
10
+ optional_keyword_parameters: [],
11
+ rest_parameter: nil,
12
+ rest_keyword_parameter: nil,
13
+ block_parameter: nil,
14
+ only_proc_parameter: false,
15
+ has_forward_parameter: false,
16
+ )
17
+
18
+ ast_node.to_a.each do |arg|
19
+ case arg.type
20
+ when :arg
21
+ parameters.positional_parameters << arg.to_a.first
22
+ when :kwarg
23
+ parameters.keyword_parameters << arg.to_a.first
24
+ when :optarg
25
+ name, value = *arg
26
+ parameters.optional_parameters << [name, from_ast(value)]
27
+ when :kwoptarg
28
+ name, value = *arg
29
+ parameters.optional_keyword_parameters << [name, from_ast(value)]
30
+ when :restarg
31
+ parameters.rest_parameter = arg.to_a.first
32
+ when :kwrestarg
33
+ parameters.rest_keyword_parameter = arg.to_a.first
34
+ when :procarg0
35
+ parameters.only_proc_parameter = true
36
+ when :blockarg
37
+ parameters.block_parameter = arg.to_a.first
38
+ when :forward_arg
39
+ parameters.has_forward_parameter = true
40
+ else
41
+ Houndstooth::Errors::Error.new(
42
+ "Unsupported argument type",
43
+ [[arg.loc.expression, "unsupported"]]
44
+ ).push
45
+ next nil
46
+ end
47
+ end
48
+
49
+ parameters
50
+ end
51
+
52
+ # True if this block of parameters takes the magic "progarc0", which in blocks can represent
53
+ # all parameters given in an array.
54
+ # @return [Boolean]
55
+ attr_accessor :only_proc_parameter
56
+
57
+ # @return [<Symbol>]
58
+ attr_accessor :positional_parameters
59
+
60
+ # @return [<(Symbol, SemanticNode)>]
61
+ attr_accessor :optional_parameters
62
+
63
+ # @return [<Symbol>]
64
+ attr_accessor :keyword_parameters
65
+
66
+ # @return [<(Symbol, SemanticNode)>]
67
+ attr_accessor :optional_keyword_parameters
68
+
69
+ # @return [Symbol, nil]
70
+ attr_accessor :rest_parameter
71
+
72
+ # @return [Symbol, nil]
73
+ attr_accessor :rest_keyword_parameter
74
+
75
+ # @return [Symbol, nil]
76
+ attr_accessor :block_parameter
77
+
78
+ # True if this method has a `...` parameter.
79
+ # @return [Boolean]
80
+ attr_accessor :has_forward_parameter
81
+
82
+ def add_to_instruction_block(block)
83
+ if optional_parameters.any? ||
84
+ keyword_parameters.any? ||
85
+ optional_keyword_parameters.any? ||
86
+ rest_parameter ||
87
+ rest_keyword_parameter ||
88
+ has_forward_parameter ||
89
+ block_parameter ||
90
+ only_proc_parameter
91
+
92
+ # Replace call with a nil
93
+ Houndstooth::Errors::Error.new(
94
+ "Only required positional parameters are supported",
95
+ [[ast_node.loc.expression, "unsupported parameters in block"]]
96
+ ).push
97
+ return false
98
+ end
99
+
100
+ # Create parameters on this block
101
+ positional_parameters.each do |name|
102
+ block.parameters << I::Variable.new(name.to_s)
103
+ end
104
+
105
+ return true
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,349 @@
1
+ module Houndstooth::SemanticNode
2
+ # A method call, called a 'send' internally by Ruby and its parser, hence its name here.
3
+ class Send < Base
4
+ # @return [SemanticNode, nil]
5
+ attr_accessor :target
6
+
7
+ # @return [Symbol]
8
+ attr_accessor :method
9
+
10
+ # @return [<SemanticNode>]
11
+ attr_accessor :arguments
12
+
13
+ # @return [Boolean]
14
+ attr_accessor :safe_navigation
15
+
16
+ # If true, this isn't really a send, but instead a super call. The `target` and `method`
17
+ # of this instance should be ignored.
18
+ # @return [Boolean]
19
+ attr_accessor :super_call
20
+
21
+ # @return [Block, nil]
22
+ attr_accessor :block
23
+
24
+ register_ast_converter :send do |ast_node, multiple_assignment_lhs: false|
25
+ target, method, *arguments_nodes = *ast_node
26
+
27
+ # Let the target shift comments first!
28
+ # This is because you can break onto newlines on the dots if you need to apply a comment
29
+ # to another node.
30
+ #
31
+ # Say you need to apply a magic comment to all of the three sends in a chain:
32
+ #
33
+ # a.b.c
34
+ #
35
+ # Appying to the first send in the chain (the "deepest target") allows you to do this:
36
+ #
37
+ # # Comment A
38
+ # a
39
+ # # Comment B
40
+ # .b
41
+ # # Comment C
42
+ # .c
43
+ #
44
+ # Rather than what you've have to do if they apply to the end of the chain:
45
+ #
46
+ # # Comment A
47
+ # _a = a
48
+ # # Comment B
49
+ # _b = a.b
50
+ # # Comment C
51
+ # _c = b.c
52
+ #
53
+ target = from_ast(target) if target
54
+ comments = shift_comments(ast_node)
55
+
56
+ if multiple_assignment_lhs
57
+ next Send.new(
58
+ ast_node: ast_node,
59
+ comments: comments,
60
+
61
+ target: target,
62
+ method: method,
63
+ arguments: [PositionalArgument.new(MagicPlaceholder.new)],
64
+ safe_navigation: false,
65
+ )
66
+ end
67
+
68
+ if arguments_nodes.last&.type == :kwargs
69
+ arguments = arguments_nodes[0...-1].map { PositionalArgument.new(from_ast(_1)) }
70
+ arguments.concat(arguments_nodes.last.to_a.map do |kwarg|
71
+ next [:_, nil] if kwarg.type == :kwsplat
72
+
73
+ unless kwarg.type == :pair
74
+ Houndstooth::Errors::Error.new(
75
+ "Expected keyword argument list to contain only pairs",
76
+ [[kwarg.loc.expression, "did not parse as a pair"]]
77
+ ).push
78
+ next nil
79
+ end
80
+
81
+ name, value = *kwarg.to_a.map { from_ast(_1) }
82
+ KeywordArgument.new(value, name: name)
83
+ end)
84
+ else
85
+ arguments = arguments_nodes.map { PositionalArgument.new(from_ast(_1)) }
86
+ end
87
+
88
+ Send.new(
89
+ ast_node: ast_node,
90
+ comments: comments,
91
+
92
+ target: target,
93
+ method: method,
94
+ arguments: arguments,
95
+ safe_navigation: false,
96
+ )
97
+ end
98
+
99
+ register_ast_converter :csend do |ast_node, multiple_assignment_lhs: false|
100
+ # Convert this csend into a send
101
+ equivalent_send_node = Parser::AST::Node.new(:send, ast_node, location: ast_node.location)
102
+
103
+ # Convert that into a semantic node and set the safe flag
104
+ send = from_ast(equivalent_send_node, multiple_assignment_lhs: multiple_assignment_lhs)
105
+ send.safe_navigation = true
106
+
107
+ send
108
+ end
109
+
110
+ register_ast_converter :block do |ast_node|
111
+ send_ast_node, args_ast_node, block_body = *ast_node
112
+
113
+ # Parse the `send`, we'll set block properties afterwards
114
+ send = from_ast(send_ast_node)
115
+ send.ast_node = ast_node
116
+
117
+ send.block = Block.new(
118
+ ast_node: ast_node,
119
+ parameters: from_ast(args_ast_node),
120
+ body: block_body.nil? ? Body.new(ast_node: ast_node) : from_ast(block_body)
121
+ )
122
+
123
+ send
124
+ end
125
+
126
+ # Numblocks are just converted into regular blocks with the same parameter names, e.g.:
127
+ #
128
+ # array.map { _1 + 1 }
129
+ #
130
+ # Becomes:
131
+ #
132
+ # array.map { |_1| _1 + 1 }
133
+ #
134
+ register_ast_converter :numblock do |ast_node|
135
+ send_ast_node, args_count, block_body = *ast_node
136
+
137
+ # Parse the `send`, we'll set block properties afterwards
138
+ send = from_ast(send_ast_node)
139
+ send.ast_node = ast_node
140
+
141
+ # Build a fake set of parameters for the block
142
+ # We need to respect the "procarg0" semantics by checking if there's only 1 parameter
143
+ if args_count == 1
144
+ parameters = Parameters.new(
145
+ ast_node: block_body,
146
+ positional_parameters: [],
147
+ optional_parameters: [],
148
+ keyword_parameters: [],
149
+ optional_keyword_parameters: [],
150
+ rest_parameter: nil,
151
+ rest_keyword_parameter: nil,
152
+ only_proc_parameter: true,
153
+ )
154
+ else
155
+ parameters = Parameters.new(
156
+ ast_node: block_body,
157
+ positional_parameters: args_count.times.map { |i| :"_#{i + 1}" },
158
+ optional_parameters: [],
159
+ keyword_parameters: [],
160
+ optional_keyword_parameters: [],
161
+ rest_parameter: nil,
162
+ rest_keyword_parameter: nil,
163
+ only_proc_parameter: false,
164
+ )
165
+ end
166
+
167
+ send.block = Block.new(
168
+ ast_node: ast_node,
169
+ parameters: parameters,
170
+ body: from_ast(block_body)
171
+ )
172
+
173
+ send
174
+ end
175
+
176
+ # Supers are virtually identical to method calls in terms of the arguments they can take.
177
+ register_ast_converter :super do |ast_node|
178
+ # Convert this super into a fake send node
179
+ equivalent_send_node = Parser::AST::Node.new(
180
+ :send,
181
+ [
182
+ # Target
183
+ nil,
184
+
185
+ # Method
186
+ :super__NOT_A_REAL_METHOD,
187
+
188
+ # Arguments
189
+ *ast_node
190
+ ],
191
+ location: ast_node.location
192
+ )
193
+
194
+ # Convert that into a semantic node and set the super flag
195
+ send = from_ast(equivalent_send_node)
196
+ send.super_call = true
197
+
198
+ send
199
+ end
200
+
201
+ def to_instructions(block)
202
+ # Generate instructions for the method's target
203
+ # If it doesn't have one, then it's implicitly `self`
204
+ if target
205
+ target.to_instructions(block)
206
+ else
207
+ block.instructions << I::SelfInstruction.new(block: block, node: self)
208
+ end
209
+ target_variable = block.instructions.last.result
210
+
211
+ type_arguments = get_type_arguments
212
+
213
+ # If this call uses save navigation, we want to wrap everything else in a conditional
214
+ # which checks the target isn't nil
215
+ # (If safe navigation bails from a call because the target is nil, the arguments don't
216
+ # get evaluated either)
217
+ if safe_navigation
218
+ # Generates:
219
+ # $1 = ...target...
220
+ # if $2.nil?
221
+ # nil
222
+ # else
223
+ # $1.method
224
+ # end
225
+ block.instructions << I::SendInstruction.new(
226
+ block: block,
227
+ node: self,
228
+ target: target_variable,
229
+ method_name: :nil?,
230
+ )
231
+
232
+ true_blk = I::InstructionBlock.new(has_scope: false, parent: block)
233
+ true_blk.instructions << I::LiteralInstruction.new(block: true_blk, node: self, value: nil)
234
+ block.instructions << I::ConditionalInstruction.new(
235
+ block: block,
236
+ node: self,
237
+ condition: block.instructions.last.result,
238
+ true_branch: true_blk,
239
+ false_branch: I::InstructionBlock.new(has_scope: false, parent: block),
240
+ )
241
+
242
+ # Replace the working instruction block with the false branch, so we insert the
243
+ # actual send in there
244
+ block = block.instructions.last.false_branch
245
+ end
246
+
247
+ # Evaluate arguments
248
+ ins_args = arguments.map do |arg|
249
+ case arg
250
+ when PositionalArgument
251
+ arg.node.to_instructions(block)
252
+ I::PositionalArgument.new(block.instructions.last.result)
253
+ when KeywordArgument
254
+ if arg.name.is_a?(SymbolLiteral) && arg.name.components.length == 1 && arg.name.components.first.is_a?(String)
255
+ arg.node.to_instructions(block)
256
+ I::KeywordArgument.new(
257
+ block.instructions.last.result,
258
+ name: arg.name.components.first,
259
+ )
260
+ else
261
+ Houndstooth::Errors::Error.new(
262
+ "Keyword argument keys must be non-interpolated symbol literals",
263
+ [[arg.name.ast_node.loc.expression, "invalid key"]]
264
+ ).push
265
+
266
+ block.instructions << I::LiteralInstruction.new(block: block, node: arg.name, value: nil)
267
+ I::KeywordArgument.new(
268
+ block.instructions.last.result,
269
+ name: "__non_symbol_key_error_#{(rand * 10000).to_i}",
270
+ )
271
+ end
272
+ else
273
+ raise "unknown node argument type: #{arg}"
274
+ end
275
+ end
276
+
277
+ # Insert send instruction
278
+ si = I::SendInstruction.new(
279
+ block: block,
280
+ node: self,
281
+ target: target_variable,
282
+ method_name: method,
283
+ arguments: ins_args,
284
+ super_call: super_call,
285
+ type_arguments: type_arguments,
286
+ )
287
+
288
+ # Build up method block
289
+ if self.block
290
+ si.method_block =
291
+ I::InstructionBlock.new(has_scope: true, parent: si).tap do |blk|
292
+ if !self.block.parameters.add_to_instruction_block(blk)
293
+ block.instructions << I::LiteralInstruction.new(node: self, block: block, value: nil)
294
+ return
295
+ end
296
+
297
+ # Create body
298
+ self.block.body.to_instructions(blk)
299
+ end
300
+ end
301
+
302
+ block.instructions << si
303
+ end
304
+ end
305
+
306
+ # A block passed to a `Send`.
307
+ class Block < Base
308
+ # @return [Parameters]
309
+ attr_accessor :parameters
310
+
311
+ # @return [SemanticNode]
312
+ attr_accessor :body
313
+ end
314
+
315
+ # An argument to a `Send`.
316
+ # @abstract
317
+ class Argument
318
+ # The node for the argument's value.
319
+ # @return [SemanticNode]
320
+ attr_accessor :node
321
+
322
+ def initialize(node)
323
+ @node = node
324
+ end
325
+ end
326
+
327
+ # A standard, singular, positional argument.
328
+ class PositionalArgument < Argument; end
329
+
330
+ # A singular keyword argument.
331
+ class KeywordArgument < Argument
332
+ # The keyword.
333
+ # @return [SemanticNode]
334
+ attr_accessor :name
335
+
336
+ def initialize(node, name:)
337
+ super(node)
338
+ @name = name
339
+ end
340
+ end
341
+
342
+ # A special argument which may appear in the arguments to a `Send`, when arguments have been
343
+ # forwarded from the enclosing method into it.
344
+ class ForwardedArguments < Base
345
+ register_ast_converter :forwarded_args do |ast_node|
346
+ ForwardedArguments.new(ast_node: ast_node)
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,12 @@
1
+ module Houndstooth::SemanticNode
2
+ # An implicit super call, without parentheses. This will forward arguments to the superclass'
3
+ # method automatically.
4
+ class ImplicitSuper < Base
5
+ register_ast_converter :zsuper do |ast_node|
6
+ self.new(ast_node: ast_node)
7
+ end
8
+ end
9
+
10
+ # ...Where's `ExplicitSuper`?
11
+ # We use `Send` for that, since the parameters are largely the same!
12
+ end
@@ -0,0 +1,119 @@
1
+ require 'parser/ruby30'
2
+
3
+ # Accuracy to Ruby 3
4
+ LEGACY_MODES = %i[lambda procarg0 encoding arg_inside_procarg0 forward_arg kwargs match_pattern]
5
+ LEGACY_MODES.each do |mode|
6
+ Parser::Builders::Default.send :"emit_#{mode}=", true
7
+ end
8
+ Parser::Builders::Default.emit_index = false
9
+ Parser::Builders::Default.emit_lambda = false
10
+
11
+ # Useful resource: https://docs.rs/lib-ruby-parser/3.0.12/lib_ruby_parser/index.html
12
+ # Based on whitequark/parser so gives good idea of what node types to expect
13
+
14
+ module Houndstooth::SemanticNode
15
+ # Shorthand for use by #to_instructions implementations
16
+ I = Houndstooth::Instructions
17
+
18
+ class Base
19
+ # @return [Parser::AST::Node]
20
+ attr_accessor :ast_node
21
+
22
+ # @return [<Parser::Source::Comment>]
23
+ attr_accessor :comments
24
+
25
+ def initialize(ast_node:, **kwargs)
26
+ @comments = []
27
+ @ast_node = ast_node
28
+
29
+ kwargs.each do |k, v|
30
+ send :"#{k}=", v
31
+ end
32
+ end
33
+
34
+ def self.from_ast(ast_node, **options)
35
+ converter = @@ast_converters[ast_node.type]
36
+
37
+ if converter.nil?
38
+ Houndstooth::Errors::Error.new(
39
+ "Unsupported AST node type #{ast_node.type}",
40
+ [[ast_node.loc.expression, "unsupported"]]
41
+ ).push
42
+ return
43
+ end
44
+
45
+ converter.(ast_node, **options)
46
+ end
47
+
48
+ def self.register_ast_converter(*types, &block)
49
+ @@ast_converters ||= {}
50
+ types.each do |type|
51
+ @@ast_converters[type] = block
52
+ end
53
+ end
54
+
55
+ # TODO: shouldn't use a global!!
56
+ def self.shift_comments(ast_node)
57
+ # TODO: don't pick *any* comment before this one, only ones on their own line
58
+ # In this case:
59
+ # x = 2 # foo
60
+ # y
61
+ # We shouldn't match the `# foo` comment to the `y` Send
62
+
63
+ if ast_node.type == :send && ast_node.location.respond_to?(:selector) && ast_node.location.selector
64
+ # Use name of the method as position reference, if available
65
+ reference_location = ast_node.location.selector
66
+ else
67
+ # Not sure what this is, just use the very start of the expression
68
+ reference_location = ast_node.location.expression
69
+ end
70
+
71
+ comments = []
72
+ comments << $comments.shift \
73
+ while $comments.first && $comments.first.location.expression < reference_location
74
+ comments
75
+ end
76
+
77
+ # Converts this semantic node into a sequence of equivalent instructions, and adds them to
78
+ # the given instruction block.
79
+ # It is expected that, after this call returns, the variable assigned by the final
80
+ # instruction in the block has an equivalent result to evaluating this expression.
81
+ # @param [InstructionBlock] block
82
+ def to_instructions(block)
83
+ raise "#to_instructions not implemented for #{self.class.name}"
84
+ end
85
+
86
+ protected
87
+
88
+ # Extracts type arguments from comments as strings.
89
+ def get_type_arguments
90
+ comments
91
+ .select { |c| c.text.start_with?('#!arg ') }
92
+ .map do |c|
93
+ unless /^#!arg\s+(.+)\s*$/ === c.text
94
+ Houndstooth::Errors::Error.new(
95
+ "Malformed #!arg definition",
96
+ [[c.loc.expression, "invalid"]]
97
+ ).push
98
+ return
99
+ end
100
+
101
+ $1
102
+ end
103
+ end
104
+ end
105
+
106
+ def self.from_ast(...)
107
+ Base.from_ast(...)
108
+ end
109
+ end
110
+
111
+ require_relative 'semantic_node/parameters'
112
+ require_relative 'semantic_node/control_flow'
113
+ require_relative 'semantic_node/operators'
114
+ require_relative 'semantic_node/identifiers'
115
+ require_relative 'semantic_node/keywords'
116
+ require_relative 'semantic_node/literals'
117
+ require_relative 'semantic_node/send'
118
+ require_relative 'semantic_node/definitions'
119
+ require_relative 'semantic_node/super'
@@ -0,0 +1,6 @@
1
+ module Houndstooth::Stdlib
2
+ def self.add_types(environment)
3
+ Houndstooth.process_file('stdlib.htt', File.read(File.join(__dir__, '..', '..', 'types', 'stdlib.htt')), environment)
4
+ environment.resolve_all_pending_types
5
+ end
6
+ end