konpeito 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 (180) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +75 -0
  4. data/CONTRIBUTING.md +123 -0
  5. data/LICENSE +21 -0
  6. data/README.md +257 -0
  7. data/Rakefile +11 -0
  8. data/bin/konpeito +6 -0
  9. data/konpeito.gemspec +43 -0
  10. data/lib/konpeito/ast/typed_ast.rb +620 -0
  11. data/lib/konpeito/ast/visitor.rb +78 -0
  12. data/lib/konpeito/cache/cache_manager.rb +230 -0
  13. data/lib/konpeito/cache/dependency_graph.rb +192 -0
  14. data/lib/konpeito/cache.rb +8 -0
  15. data/lib/konpeito/cli/base_command.rb +187 -0
  16. data/lib/konpeito/cli/build_command.rb +220 -0
  17. data/lib/konpeito/cli/check_command.rb +104 -0
  18. data/lib/konpeito/cli/config.rb +231 -0
  19. data/lib/konpeito/cli/deps_command.rb +128 -0
  20. data/lib/konpeito/cli/doctor_command.rb +340 -0
  21. data/lib/konpeito/cli/fmt_command.rb +199 -0
  22. data/lib/konpeito/cli/init_command.rb +312 -0
  23. data/lib/konpeito/cli/lsp_command.rb +40 -0
  24. data/lib/konpeito/cli/run_command.rb +150 -0
  25. data/lib/konpeito/cli/test_command.rb +248 -0
  26. data/lib/konpeito/cli/watch_command.rb +212 -0
  27. data/lib/konpeito/cli.rb +301 -0
  28. data/lib/konpeito/codegen/builtin_methods.rb +229 -0
  29. data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
  30. data/lib/konpeito/codegen/debug_info.rb +352 -0
  31. data/lib/konpeito/codegen/inliner.rb +486 -0
  32. data/lib/konpeito/codegen/jvm_backend.rb +197 -0
  33. data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
  34. data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
  35. data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
  36. data/lib/konpeito/codegen/monomorphizer.rb +359 -0
  37. data/lib/konpeito/codegen/profile_runtime.c +341 -0
  38. data/lib/konpeito/codegen/profiler.rb +99 -0
  39. data/lib/konpeito/compiler.rb +592 -0
  40. data/lib/konpeito/dependency_resolver.rb +296 -0
  41. data/lib/konpeito/diagnostics/collector.rb +127 -0
  42. data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
  43. data/lib/konpeito/diagnostics/renderer.rb +144 -0
  44. data/lib/konpeito/formatter/formatter.rb +1214 -0
  45. data/lib/konpeito/hir/builder.rb +7167 -0
  46. data/lib/konpeito/hir/nodes.rb +2465 -0
  47. data/lib/konpeito/lsp/document_manager.rb +820 -0
  48. data/lib/konpeito/lsp/server.rb +183 -0
  49. data/lib/konpeito/lsp/transport.rb +38 -0
  50. data/lib/konpeito/parser/prism_adapter.rb +65 -0
  51. data/lib/konpeito/platform.rb +103 -0
  52. data/lib/konpeito/profile/report.rb +136 -0
  53. data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
  54. data/lib/konpeito/stdlib/compression/compression.rb +72 -0
  55. data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
  56. data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
  57. data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
  58. data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
  59. data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
  60. data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
  61. data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
  62. data/lib/konpeito/stdlib/http/extconf.rb +19 -0
  63. data/lib/konpeito/stdlib/http/http.rb +125 -0
  64. data/lib/konpeito/stdlib/http/http.rbs +57 -0
  65. data/lib/konpeito/stdlib/http/http_native.c +440 -0
  66. data/lib/konpeito/stdlib/json/extconf.rb +17 -0
  67. data/lib/konpeito/stdlib/json/json.rb +44 -0
  68. data/lib/konpeito/stdlib/json/json.rbs +33 -0
  69. data/lib/konpeito/stdlib/json/json_native.c +286 -0
  70. data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
  71. data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
  72. data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
  73. data/lib/konpeito/stdlib/ui/ui.rb +318 -0
  74. data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
  75. data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
  76. data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
  77. data/lib/konpeito/type_checker/inferrer.rb +565 -0
  78. data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
  79. data/lib/konpeito/type_checker/type_resolver.rb +276 -0
  80. data/lib/konpeito/type_checker/types.rb +1434 -0
  81. data/lib/konpeito/type_checker/unification.rb +323 -0
  82. data/lib/konpeito/ui/animation/animated_state.rb +80 -0
  83. data/lib/konpeito/ui/animation/easing.rb +59 -0
  84. data/lib/konpeito/ui/animation/value_tween.rb +66 -0
  85. data/lib/konpeito/ui/app.rb +379 -0
  86. data/lib/konpeito/ui/box.rb +38 -0
  87. data/lib/konpeito/ui/castella.rb +70 -0
  88. data/lib/konpeito/ui/castella_native.rb +76 -0
  89. data/lib/konpeito/ui/chart/area_chart.rb +305 -0
  90. data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
  91. data/lib/konpeito/ui/chart/base_chart.rb +210 -0
  92. data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
  93. data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
  94. data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
  95. data/lib/konpeito/ui/chart/line_chart.rb +289 -0
  96. data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
  97. data/lib/konpeito/ui/chart/scales.rb +77 -0
  98. data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
  99. data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
  100. data/lib/konpeito/ui/column.rb +271 -0
  101. data/lib/konpeito/ui/core.rb +2199 -0
  102. data/lib/konpeito/ui/dsl.rb +443 -0
  103. data/lib/konpeito/ui/frame.rb +171 -0
  104. data/lib/konpeito/ui/frame_native.rb +494 -0
  105. data/lib/konpeito/ui/markdown/ast.rb +124 -0
  106. data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
  107. data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
  108. data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
  109. data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
  110. data/lib/konpeito/ui/markdown/parser.rb +805 -0
  111. data/lib/konpeito/ui/markdown/renderer.rb +639 -0
  112. data/lib/konpeito/ui/markdown/theme.rb +165 -0
  113. data/lib/konpeito/ui/render_node.rb +260 -0
  114. data/lib/konpeito/ui/row.rb +207 -0
  115. data/lib/konpeito/ui/spacer.rb +18 -0
  116. data/lib/konpeito/ui/style.rb +799 -0
  117. data/lib/konpeito/ui/theme.rb +563 -0
  118. data/lib/konpeito/ui/themes/material.rb +35 -0
  119. data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
  120. data/lib/konpeito/ui/widgets/button.rb +103 -0
  121. data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
  122. data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
  123. data/lib/konpeito/ui/widgets/container.rb +91 -0
  124. data/lib/konpeito/ui/widgets/data_table.rb +667 -0
  125. data/lib/konpeito/ui/widgets/divider.rb +29 -0
  126. data/lib/konpeito/ui/widgets/image.rb +105 -0
  127. data/lib/konpeito/ui/widgets/input.rb +485 -0
  128. data/lib/konpeito/ui/widgets/markdown.rb +57 -0
  129. data/lib/konpeito/ui/widgets/modal.rb +163 -0
  130. data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
  131. data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
  132. data/lib/konpeito/ui/widgets/net_image.rb +100 -0
  133. data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
  134. data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
  135. data/lib/konpeito/ui/widgets/slider.rb +133 -0
  136. data/lib/konpeito/ui/widgets/switch.rb +84 -0
  137. data/lib/konpeito/ui/widgets/tabs.rb +157 -0
  138. data/lib/konpeito/ui/widgets/text.rb +110 -0
  139. data/lib/konpeito/ui/widgets/tree.rb +426 -0
  140. data/lib/konpeito/version.rb +5 -0
  141. data/lib/konpeito.rb +109 -0
  142. data/test_native_array.rb +172 -0
  143. data/test_native_array_class.rb +197 -0
  144. data/test_native_class.rb +151 -0
  145. data/tools/konpeito-asm/build.sh +65 -0
  146. data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
  147. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  148. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
  149. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  150. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
  151. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
  152. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
  153. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  154. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
  155. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
  156. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
  157. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
  158. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
  159. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  160. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
  161. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
  162. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  163. data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
  164. data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
  165. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
  166. data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
  167. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
  168. data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
  169. data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
  170. data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
  171. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
  172. data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
  173. data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
  174. data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
  175. data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
  176. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
  177. data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
  178. data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
  179. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
  180. metadata +267 -0
@@ -0,0 +1,1434 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module TypeChecker
5
+ # Internal type representation for the compiler
6
+ module Types
7
+ # Base class for all types
8
+ class Type
9
+ def ==(other)
10
+ self.class == other.class
11
+ end
12
+
13
+ def hash
14
+ self.class.hash
15
+ end
16
+
17
+ def eql?(other)
18
+ self == other
19
+ end
20
+
21
+ def untyped?
22
+ false
23
+ end
24
+
25
+ def union?
26
+ false
27
+ end
28
+
29
+ # Check if this type can be used where `other` is expected
30
+ def subtype_of?(other)
31
+ return true if other.is_a?(Untyped)
32
+ return true if self == other
33
+ false
34
+ end
35
+ end
36
+
37
+ # Unknown/any type
38
+ class Untyped < Type
39
+ def to_s
40
+ "untyped"
41
+ end
42
+
43
+ def untyped?
44
+ true
45
+ end
46
+
47
+ def subtype_of?(_other)
48
+ true
49
+ end
50
+ end
51
+
52
+ # Bottom type (no possible value)
53
+ class Bottom < Type
54
+ def to_s
55
+ "bot"
56
+ end
57
+
58
+ def subtype_of?(_other)
59
+ true
60
+ end
61
+ end
62
+
63
+ # Nil type
64
+ class NilType < Type
65
+ def to_s
66
+ "nil"
67
+ end
68
+ end
69
+
70
+ # Boolean type (true | false)
71
+ class BoolType < Type
72
+ def to_s
73
+ "bool"
74
+ end
75
+ end
76
+
77
+ # Represents a specific class instance
78
+ class ClassInstance < Type
79
+ attr_reader :name, :type_args
80
+
81
+ def initialize(name, type_args = [])
82
+ @name = name.to_sym
83
+ @type_args = type_args
84
+ end
85
+
86
+ def ==(other)
87
+ return false unless other.is_a?(ClassInstance)
88
+ name == other.name && type_args == other.type_args
89
+ end
90
+
91
+ def hash
92
+ [name, type_args].hash
93
+ end
94
+
95
+ def to_s
96
+ if type_args.empty?
97
+ name.to_s
98
+ else
99
+ "#{name}[#{type_args.map(&:to_s).join(", ")}]"
100
+ end
101
+ end
102
+
103
+ def subtype_of?(other)
104
+ return true if super
105
+ return false unless other.is_a?(ClassInstance)
106
+ return true if name == other.name
107
+
108
+ # Check both built-in and user-defined class hierarchy
109
+ ancestors = ClassInstance.lookup_hierarchy(name)
110
+ return false unless ancestors
111
+ ancestors.include?(other.name.to_sym)
112
+ end
113
+
114
+ # Get ancestors list for a class (both built-in and user-defined)
115
+ def self.lookup_hierarchy(class_name)
116
+ CLASS_HIERARCHY[class_name.to_sym] || @@user_class_hierarchy[class_name.to_sym]
117
+ end
118
+
119
+ # Register user-defined class hierarchy
120
+ def self.register_class_hierarchy(class_name, ancestors)
121
+ @@user_class_hierarchy[class_name.to_sym] = ancestors.map(&:to_sym)
122
+ end
123
+
124
+ # Reset user-defined hierarchies (for test isolation)
125
+ def self.reset_user_hierarchy!
126
+ @@user_class_hierarchy.clear
127
+ end
128
+
129
+ # Find LUB (Least Upper Bound) of two class types
130
+ def self.find_lub(t1, t2)
131
+ return nil unless t1.is_a?(ClassInstance) && t2.is_a?(ClassInstance)
132
+
133
+ ancestors1 = [t1.name] + (lookup_hierarchy(t1.name) || [:Object])
134
+ ancestors2 = [t2.name] + (lookup_hierarchy(t2.name) || [:Object])
135
+
136
+ # Find first common ancestor
137
+ ancestors1.each do |a|
138
+ return ClassInstance.new(a) if ancestors2.include?(a)
139
+ end
140
+ ClassInstance.new(:Object)
141
+ end
142
+
143
+ # User-defined class hierarchies (mutable, unlike CLASS_HIERARCHY)
144
+ @@user_class_hierarchy = {}
145
+
146
+ # Basic Ruby class hierarchy
147
+ CLASS_HIERARCHY = {
148
+ Integer: [:Numeric, :Object, :BasicObject],
149
+ Float: [:Numeric, :Object, :BasicObject],
150
+ Rational: [:Numeric, :Object, :BasicObject],
151
+ Complex: [:Numeric, :Object, :BasicObject],
152
+ Numeric: [:Object, :BasicObject],
153
+ String: [:Object, :BasicObject],
154
+ Symbol: [:Object, :BasicObject],
155
+ Array: [:Object, :BasicObject],
156
+ Hash: [:Object, :BasicObject],
157
+ TrueClass: [:Object, :BasicObject],
158
+ FalseClass: [:Object, :BasicObject],
159
+ NilClass: [:Object, :BasicObject],
160
+ Range: [:Object, :BasicObject],
161
+ Regexp: [:Object, :BasicObject],
162
+ Proc: [:Object, :BasicObject],
163
+ Fiber: [:Object, :BasicObject],
164
+ Thread: [:Object, :BasicObject],
165
+ Mutex: [:Object, :BasicObject],
166
+ Queue: [:Object, :BasicObject],
167
+ SizedQueue: [:Queue, :Object, :BasicObject],
168
+ ConditionVariable: [:Object, :BasicObject],
169
+ Time: [:Object, :BasicObject],
170
+ StandardError: [:Exception, :Object, :BasicObject],
171
+ RuntimeError: [:StandardError, :Exception, :Object, :BasicObject],
172
+ TypeError: [:StandardError, :Exception, :Object, :BasicObject],
173
+ ArgumentError: [:StandardError, :Exception, :Object, :BasicObject],
174
+ NameError: [:StandardError, :Exception, :Object, :BasicObject],
175
+ NoMethodError: [:NameError, :StandardError, :Exception, :Object, :BasicObject],
176
+ IOError: [:StandardError, :Exception, :Object, :BasicObject],
177
+ ZeroDivisionError: [:StandardError, :Exception, :Object, :BasicObject],
178
+ Exception: [:Object, :BasicObject],
179
+ Object: [:BasicObject],
180
+ BasicObject: [],
181
+ }.freeze
182
+ end
183
+
184
+ # Represents a class/module itself (for class method calls)
185
+ # e.g., when we write `NativeHash.new`, the receiver type is ClassSingleton(:NativeHash)
186
+ class ClassSingleton < Type
187
+ attr_reader :name
188
+
189
+ def initialize(name)
190
+ @name = name.to_sym
191
+ end
192
+
193
+ def ==(other)
194
+ return false unless other.is_a?(ClassSingleton)
195
+ name == other.name
196
+ end
197
+
198
+ def hash
199
+ [:singleton, name].hash
200
+ end
201
+
202
+ def to_s
203
+ "singleton(#{name})"
204
+ end
205
+ end
206
+
207
+ # Union of types (A | B)
208
+ class Union < Type
209
+ attr_reader :types
210
+
211
+ def initialize(types)
212
+ @types = types.uniq
213
+ end
214
+
215
+ def ==(other)
216
+ return false unless other.is_a?(Union)
217
+ types.sort_by(&:to_s) == other.types.sort_by(&:to_s)
218
+ end
219
+
220
+ def hash
221
+ types.map(&:hash).sort.hash
222
+ end
223
+
224
+ def union?
225
+ true
226
+ end
227
+
228
+ def to_s
229
+ types.map(&:to_s).join(" | ")
230
+ end
231
+
232
+ def subtype_of?(other)
233
+ return true if super
234
+ return types.all? { |t| t.subtype_of?(other) } unless other.is_a?(Union)
235
+ types.all? { |t| other.types.any? { |o| t.subtype_of?(o) } }
236
+ end
237
+ end
238
+
239
+ # Intersection of types (A & B)
240
+ class Intersection < Type
241
+ attr_reader :types
242
+
243
+ def initialize(types)
244
+ @types = types.uniq
245
+ end
246
+
247
+ def ==(other)
248
+ return false unless other.is_a?(Intersection)
249
+ types.sort_by(&:to_s) == other.types.sort_by(&:to_s)
250
+ end
251
+
252
+ def hash
253
+ types.map(&:hash).sort.hash
254
+ end
255
+
256
+ def to_s
257
+ types.map(&:to_s).join(" & ")
258
+ end
259
+ end
260
+
261
+ # Proc/Lambda type
262
+ class ProcType < Type
263
+ attr_reader :param_types, :return_type
264
+
265
+ def initialize(param_types, return_type)
266
+ @param_types = param_types
267
+ @return_type = return_type
268
+ end
269
+
270
+ def ==(other)
271
+ return false unless other.is_a?(ProcType)
272
+ param_types == other.param_types && return_type == other.return_type
273
+ end
274
+
275
+ def hash
276
+ [param_types, return_type].hash
277
+ end
278
+
279
+ def to_s
280
+ params = param_types.map(&:to_s).join(", ")
281
+ "^(#{params}) -> #{return_type}"
282
+ end
283
+ end
284
+
285
+ # NativeArray type - contiguous memory array with unboxed elements
286
+ # Supports numeric types (Int64, Float64) and NativeClass types
287
+ class NativeArrayType < Type
288
+ attr_reader :element_type
289
+
290
+ # Allowed primitive element types for NativeArray
291
+ ALLOWED_PRIMITIVES = %i[Int64 Float64].freeze
292
+
293
+ # @param element_type [Symbol, NativeClassType] Element type
294
+ def initialize(element_type)
295
+ @element_type = element_type
296
+ validate_element_type!
297
+ end
298
+
299
+ def ==(other)
300
+ return false unless other.is_a?(NativeArrayType)
301
+ element_type == other.element_type
302
+ end
303
+
304
+ def hash
305
+ elem_hash = native_class_element? ? element_type.name : element_type
306
+ [:NativeArray, elem_hash].hash
307
+ end
308
+
309
+ def to_s
310
+ elem_str = native_class_element? ? element_type.name : element_type
311
+ "NativeArray[#{elem_str}]"
312
+ end
313
+
314
+ def int64?
315
+ element_type == :Int64
316
+ end
317
+
318
+ def float64?
319
+ element_type == :Float64
320
+ end
321
+
322
+ def native_class_element?
323
+ element_type.is_a?(NativeClassType)
324
+ end
325
+
326
+ def primitive_element?
327
+ ALLOWED_PRIMITIVES.include?(element_type)
328
+ end
329
+
330
+ # Get LLVM type tag for primitive elements
331
+ def llvm_element_type_tag
332
+ return :native_class if native_class_element?
333
+ int64? ? :i64 : :double
334
+ end
335
+
336
+ private
337
+
338
+ def validate_element_type!
339
+ return if ALLOWED_PRIMITIVES.include?(element_type)
340
+ return if element_type.is_a?(NativeClassType)
341
+
342
+ raise ArgumentError,
343
+ "NativeArray only supports #{ALLOWED_PRIMITIVES.join(', ')} or NativeClass, got #{element_type}"
344
+ end
345
+ end
346
+
347
+ # StaticArray type - fixed-size stack-allocated array with compile-time size
348
+ # Similar to NativeArray but size is known at compile time
349
+ # Enables stack allocation (no heap) and potential optimizations
350
+ class StaticArrayType < Type
351
+ attr_reader :element_type, :size
352
+
353
+ # Allowed primitive element types for StaticArray
354
+ ALLOWED_PRIMITIVES = %i[Int64 Float64].freeze
355
+
356
+ # @param element_type [Symbol] Element type (:Int64 or :Float64)
357
+ # @param size [Integer] Array size (compile-time constant)
358
+ def initialize(element_type, size)
359
+ @element_type = element_type
360
+ @size = size
361
+ validate!
362
+ end
363
+
364
+ def ==(other)
365
+ return false unless other.is_a?(StaticArrayType)
366
+ element_type == other.element_type && size == other.size
367
+ end
368
+
369
+ def hash
370
+ [:StaticArray, element_type, size].hash
371
+ end
372
+
373
+ def to_s
374
+ "StaticArray[#{element_type}, #{size}]"
375
+ end
376
+
377
+ def int64?
378
+ element_type == :Int64
379
+ end
380
+
381
+ def float64?
382
+ element_type == :Float64
383
+ end
384
+
385
+ # Get LLVM type tag for elements
386
+ def llvm_element_type_tag
387
+ int64? ? :i64 : :double
388
+ end
389
+
390
+ # Get total byte size
391
+ def byte_size
392
+ size * 8 # 8 bytes per element (i64 or double)
393
+ end
394
+
395
+ private
396
+
397
+ def validate!
398
+ unless ALLOWED_PRIMITIVES.include?(element_type)
399
+ raise ArgumentError,
400
+ "StaticArray only supports #{ALLOWED_PRIMITIVES.join(', ')}, got #{element_type}"
401
+ end
402
+ unless size.is_a?(Integer) && size > 0
403
+ raise ArgumentError, "StaticArray size must be a positive integer, got #{size}"
404
+ end
405
+ end
406
+ end
407
+
408
+ # ByteBuffer type - growable byte array for efficient I/O operations
409
+ # Provides direct memory access and search operations (memchr)
410
+ # Used for HTTP request/response parsing and building
411
+ class ByteBufferType < Type
412
+ attr_reader :capacity
413
+
414
+ # @param capacity [Integer, nil] Optional capacity hint (nil = dynamic)
415
+ def initialize(capacity: nil)
416
+ @capacity = capacity
417
+ end
418
+
419
+ def ==(other)
420
+ other.is_a?(ByteBufferType)
421
+ end
422
+
423
+ def hash
424
+ :ByteBuffer.hash
425
+ end
426
+
427
+ def to_s
428
+ capacity ? "ByteBuffer[#{capacity}]" : "ByteBuffer"
429
+ end
430
+
431
+ def static_capacity?
432
+ !@capacity.nil?
433
+ end
434
+ end
435
+
436
+ # ByteSlice type - zero-copy view into a ByteBuffer
437
+ # Provides read-only access to a portion of buffer memory
438
+ class ByteSliceType < Type
439
+ def ==(other)
440
+ other.is_a?(ByteSliceType)
441
+ end
442
+
443
+ def hash
444
+ :ByteSlice.hash
445
+ end
446
+
447
+ def to_s
448
+ "ByteSlice"
449
+ end
450
+ end
451
+
452
+ # Slice[T] type - bounds-checked pointer view to contiguous memory
453
+ # Generic version of ByteSlice supporting Int64 and Float64 elements
454
+ # Memory layout: { ptr, size } - 16 bytes on 64-bit systems
455
+ # Used for zero-copy views into NativeArray and StaticArray
456
+ class SliceType < Type
457
+ attr_reader :element_type
458
+
459
+ # Allowed primitive element types for Slice
460
+ ALLOWED_PRIMITIVES = %i[Int64 Float64].freeze
461
+
462
+ # @param element_type [Symbol] Element type (:Int64 or :Float64)
463
+ def initialize(element_type)
464
+ @element_type = element_type
465
+ validate!
466
+ end
467
+
468
+ def ==(other)
469
+ return false unless other.is_a?(SliceType)
470
+ element_type == other.element_type
471
+ end
472
+
473
+ def hash
474
+ [:Slice, element_type].hash
475
+ end
476
+
477
+ def to_s
478
+ "Slice[#{element_type}]"
479
+ end
480
+
481
+ def int64?
482
+ element_type == :Int64
483
+ end
484
+
485
+ def float64?
486
+ element_type == :Float64
487
+ end
488
+
489
+ # Get LLVM type tag for elements
490
+ def llvm_element_type_tag
491
+ int64? ? :i64 : :double
492
+ end
493
+
494
+ # Get element size in bytes
495
+ def element_size
496
+ 8 # Both i64 and double are 8 bytes
497
+ end
498
+
499
+ private
500
+
501
+ def validate!
502
+ unless ALLOWED_PRIMITIVES.include?(element_type)
503
+ raise ArgumentError,
504
+ "Slice only supports #{ALLOWED_PRIMITIVES.join(', ')}, got #{element_type}"
505
+ end
506
+ end
507
+ end
508
+
509
+ # NativeHash[K, V] type - typed hash map with unboxed values
510
+ # Supports String/Symbol/Integer keys and Integer/Float/Bool/String/NativeClass values
511
+ # Memory layout: { capacity: i64, size: i64, buckets: ptr } - 24 bytes header
512
+ # Uses Robin Hood hashing for efficient lookup
513
+ class NativeHashType < Type
514
+ attr_reader :key_type, :value_type
515
+
516
+ # Allowed key types (must be hashable)
517
+ ALLOWED_KEY_TYPES = %i[String Symbol Integer].freeze
518
+
519
+ # Allowed primitive value types (unboxed)
520
+ ALLOWED_PRIMITIVE_VALUES = %i[Integer Float Bool].freeze
521
+
522
+ # Ruby object value types (boxed, require GC marking)
523
+ RUBY_OBJECT_VALUES = %i[String Object Array Hash].freeze
524
+
525
+ # @param key_type [Symbol] Key type (:String, :Symbol, :Integer)
526
+ # @param value_type [Symbol, NativeClassType] Value type
527
+ def initialize(key_type, value_type)
528
+ @key_type = key_type.to_sym
529
+ @value_type = value_type.is_a?(NativeClassType) ? value_type : value_type.to_sym
530
+ validate!
531
+ end
532
+
533
+ def ==(other)
534
+ return false unless other.is_a?(NativeHashType)
535
+ key_type == other.key_type && value_type == other.value_type
536
+ end
537
+
538
+ def hash
539
+ val_hash = native_class_value? ? value_type.name : value_type
540
+ [:NativeHash, key_type, val_hash].hash
541
+ end
542
+
543
+ def to_s
544
+ val_str = native_class_value? ? value_type.name : value_type
545
+ "NativeHash[#{key_type}, #{val_str}]"
546
+ end
547
+
548
+ # Check if key type is String
549
+ def string_key?
550
+ key_type == :String
551
+ end
552
+
553
+ # Check if key type is Symbol
554
+ def symbol_key?
555
+ key_type == :Symbol
556
+ end
557
+
558
+ # Check if key type is Integer
559
+ def integer_key?
560
+ key_type == :Integer
561
+ end
562
+
563
+ # Check if value type is a primitive (unboxed)
564
+ def primitive_value?
565
+ ALLOWED_PRIMITIVE_VALUES.include?(value_type)
566
+ end
567
+
568
+ # Check if value type is a NativeClass
569
+ def native_class_value?
570
+ value_type.is_a?(NativeClassType)
571
+ end
572
+
573
+ # Check if value type is a Ruby object (boxed)
574
+ def ruby_object_value?
575
+ RUBY_OBJECT_VALUES.include?(value_type)
576
+ end
577
+
578
+ # Get LLVM type tag for key
579
+ def llvm_key_type_tag
580
+ case key_type
581
+ when :String then :value # Ruby String VALUE
582
+ when :Symbol then :i64 # Symbol ID
583
+ when :Integer then :i64 # Unboxed integer
584
+ end
585
+ end
586
+
587
+ # Get LLVM type tag for value
588
+ def llvm_value_type_tag
589
+ case value_type
590
+ when :Integer then :i64
591
+ when :Float then :double
592
+ when :Bool then :i8
593
+ when :String, :Object, :Array, :Hash then :value
594
+ else
595
+ native_class_value? ? :native_class : :value
596
+ end
597
+ end
598
+
599
+ # Get key size in bytes
600
+ def key_size
601
+ 8 # All key types are 8 bytes (VALUE or i64)
602
+ end
603
+
604
+ # Get value size in bytes
605
+ def value_size
606
+ case value_type
607
+ when :Bool then 1
608
+ else 8 # i64, double, or VALUE pointer
609
+ end
610
+ end
611
+
612
+ # Get entry size in bytes (hash + key + value + state, aligned)
613
+ def entry_size
614
+ # hash(8) + key(8) + value(8) + state(1) + padding(7) = 32
615
+ 32
616
+ end
617
+
618
+ private
619
+
620
+ def validate!
621
+ unless ALLOWED_KEY_TYPES.include?(key_type)
622
+ raise ArgumentError,
623
+ "NativeHash key must be #{ALLOWED_KEY_TYPES.join(', ')}, got #{key_type}"
624
+ end
625
+
626
+ unless primitive_value? || ruby_object_value? || native_class_value?
627
+ raise ArgumentError,
628
+ "NativeHash value must be a primitive, Ruby object, or NativeClass, got #{value_type}"
629
+ end
630
+ end
631
+ end
632
+
633
+ # StringBuffer type - efficient string building with pre-allocation
634
+ # Wraps CRuby's rb_str_buf_new for optimized string concatenation
635
+ class StringBufferType < Type
636
+ attr_reader :capacity
637
+
638
+ # @param capacity [Integer, nil] Optional initial capacity
639
+ def initialize(capacity: nil)
640
+ @capacity = capacity
641
+ end
642
+
643
+ def ==(other)
644
+ other.is_a?(StringBufferType)
645
+ end
646
+
647
+ def hash
648
+ :StringBuffer.hash
649
+ end
650
+
651
+ def to_s
652
+ capacity ? "StringBuffer[#{capacity}]" : "StringBuffer"
653
+ end
654
+ end
655
+
656
+ # NativeString type - UTF-8 native string with optimized operations
657
+ # Provides both byte-level (O(1)) and character-level (UTF-8 aware) operations
658
+ # Memory layout: { ptr (i8*), byte_len (i64), char_len (i64), flags (i64) }
659
+ # Flags: bit 0 = ASCII_ONLY (when set, byte_len == char_len)
660
+ class NativeStringType < Type
661
+ # Encoding mode for the string
662
+ ENCODING_UTF8 = :utf8
663
+ ENCODING_ASCII = :ascii
664
+
665
+ def ==(other)
666
+ other.is_a?(NativeStringType)
667
+ end
668
+
669
+ def hash
670
+ :NativeString.hash
671
+ end
672
+
673
+ def to_s
674
+ "NativeString"
675
+ end
676
+
677
+ # Check if this is known to be ASCII-only at compile time
678
+ # Runtime check uses ascii_only? method
679
+ def ascii_only?
680
+ false # Conservative default; runtime determines actual encoding
681
+ end
682
+ end
683
+
684
+ # NativeClass type - fixed layout struct with unboxed numeric fields
685
+ # Marked with @native annotation in RBS
686
+ # Supports Wren-style single inheritance and instance methods
687
+ # Optionally supports vtable polymorphism with @native vtable annotation
688
+ # Can be marked as value type with @struct annotation for pass-by-value semantics
689
+ class NativeClassType < Type
690
+ attr_reader :name, :fields, :methods, :superclass
691
+ attr_accessor :vtable, :is_value_type
692
+
693
+ # Allowed primitive field types for NativeClass (unboxed)
694
+ ALLOWED_PRIMITIVE_TYPES = %i[Int64 Float64 Bool].freeze
695
+
696
+ # Ruby object field types (boxed, require GC marking)
697
+ RUBY_OBJECT_TYPES = %i[String Object Array Hash].freeze
698
+
699
+ # @param name [Symbol] Class name
700
+ # @param fields [Hash{Symbol => Symbol}] Field name -> type (:Int64, :Float64, :Bool, or NativeClass name)
701
+ # @param methods [Hash{Symbol => NativeMethodType}] Method name -> signature
702
+ # @param superclass [Symbol, nil] Superclass name (nil for root classes)
703
+ # @param vtable [Boolean] Whether to use vtable for dynamic dispatch
704
+ # @param is_value_type [Boolean] Whether this is a value type (@struct annotation)
705
+ # @param native_class_registry [Hash{Symbol => NativeClassType}] Registry for validating embedded types
706
+ def initialize(name, fields = {}, methods = {}, superclass: nil, vtable: false, is_value_type: false, native_class_registry: nil)
707
+ @name = name.to_sym
708
+ @fields = fields
709
+ @methods = methods
710
+ @superclass = superclass&.to_sym
711
+ @vtable = vtable
712
+ @is_value_type = is_value_type
713
+ @native_class_registry = native_class_registry
714
+ # Validation is deferred - call validate_fields! after all classes are registered
715
+ end
716
+
717
+ # Set registry for field validation (called after all classes are parsed)
718
+ def native_class_registry=(registry)
719
+ @native_class_registry = registry
720
+ end
721
+
722
+ def ==(other)
723
+ return false unless other.is_a?(NativeClassType)
724
+ name == other.name && fields == other.fields && superclass == other.superclass && vtable == other.vtable
725
+ end
726
+
727
+ def hash
728
+ [name, fields, superclass, vtable].hash
729
+ end
730
+
731
+ # Check if this class uses vtable dispatch (or inherits from one that does)
732
+ def uses_vtable?(registry = nil)
733
+ return true if @vtable
734
+ return false unless @superclass
735
+
736
+ reg = registry || @native_class_registry || {}
737
+ parent = reg[@superclass]
738
+ parent&.uses_vtable?(reg) || false
739
+ end
740
+
741
+ # Get all methods for vtable layout (parent methods first, then own methods)
742
+ # Method order is important: parent class methods must be at the same index
743
+ # in child vtables for polymorphism to work correctly.
744
+ # @param registry [Hash{Symbol => NativeClassType}] Registry of all native classes
745
+ # @return [Array<[Symbol, NativeMethodType, Symbol]>] Array of [method_name, method_sig, owner_class_name]
746
+ def vtable_methods(registry)
747
+ inherited = if @superclass
748
+ parent = registry[@superclass]
749
+ parent ? parent.vtable_methods(registry) : []
750
+ else
751
+ []
752
+ end
753
+
754
+ # Build own methods list, excluding those already in parent vtable
755
+ parent_method_names = inherited.map { |m| m[0] }.to_set
756
+ own = @methods.map do |method_name, method_sig|
757
+ [method_name, method_sig, @name]
758
+ end.reject { |m| parent_method_names.include?(m[0]) }
759
+
760
+ # For overridden methods, replace parent entries with own implementation
761
+ result = inherited.map do |parent_entry|
762
+ method_name = parent_entry[0]
763
+ if @methods.key?(method_name)
764
+ # Override: use child's implementation but keep same vtable index
765
+ [method_name, @methods[method_name], @name]
766
+ else
767
+ parent_entry
768
+ end
769
+ end
770
+
771
+ result + own
772
+ end
773
+
774
+ # Get vtable index for a method
775
+ # @param method_name [Symbol] Method name
776
+ # @param registry [Hash{Symbol => NativeClassType}] Registry of all native classes
777
+ # @return [Integer, nil] Vtable index (0-based) or nil if method not in vtable
778
+ def vtable_index(method_name, registry)
779
+ methods = vtable_methods(registry)
780
+ methods.index { |m| m[0] == method_name.to_sym }
781
+ end
782
+
783
+ # Get the vtable size (number of methods)
784
+ def vtable_size(registry)
785
+ vtable_methods(registry).size
786
+ end
787
+
788
+ def to_s
789
+ field_strs = fields.map { |n, t| "@#{n}: #{t}" }.join(", ")
790
+ method_strs = methods.keys.map(&:to_s).join(", ")
791
+ base = superclass ? " < #{superclass}" : ""
792
+ "NativeClass[#{name}#{base}]{#{field_strs}}[#{method_strs}]"
793
+ end
794
+
795
+ # Get field type by name (own fields only)
796
+ def field_type(field_name)
797
+ fields[field_name.to_sym]
798
+ end
799
+
800
+ # Get field index for struct layout (includes inherited fields)
801
+ # @param field_name [Symbol, String] Field name
802
+ # @param registry [Hash{Symbol => NativeClassType}] Registry of all native classes
803
+ def field_index(field_name, registry = nil)
804
+ all = all_fields(registry || {})
805
+ all.keys.index(field_name.to_sym)
806
+ end
807
+
808
+ # Get total number of fields (including inherited)
809
+ def field_count(registry = nil)
810
+ all_fields(registry || {}).size
811
+ end
812
+
813
+ # Calculate byte size (assuming 8 bytes per field, including inherited)
814
+ def byte_size(registry = nil)
815
+ all_fields(registry || {}).size * 8
816
+ end
817
+
818
+ # Check if this is a value type (pass-by-value semantics)
819
+ def is_value_type?
820
+ @is_value_type == true
821
+ end
822
+
823
+ # Validate that this type can be used as a value type
824
+ # Returns [valid, error_message]
825
+ def valid_value_type?(registry = nil)
826
+ reg = registry || @native_class_registry || {}
827
+
828
+ # Check for VALUE fields (String, Array, Hash, Object)
829
+ all = all_fields(reg)
830
+ value_fields = all.select { |_, type| RUBY_OBJECT_TYPES.include?(type) || type.is_a?(Hash) }
831
+ unless value_fields.empty?
832
+ return [false, "Value type cannot have Ruby object fields: #{value_fields.keys.join(', ')}"]
833
+ end
834
+
835
+ # Check size limit (128 bytes max for efficient register passing)
836
+ size = byte_size(reg)
837
+ if size > 128
838
+ return [false, "Value type too large (#{size} bytes > 128 bytes max)"]
839
+ end
840
+
841
+ # Check for superclass (value types cannot have inheritance for simplicity)
842
+ if @superclass
843
+ return [false, "Value types cannot have superclass"]
844
+ end
845
+
846
+ [true, nil]
847
+ end
848
+
849
+ # Get LLVM type tag for a field
850
+ def llvm_field_type_tag(field_name, registry = nil)
851
+ reg = registry || @native_class_registry || {}
852
+ all = all_fields(reg)
853
+ field_type = all[field_name.to_sym]
854
+ case field_type
855
+ when :Int64 then :i64
856
+ when :Float64 then :double
857
+ when :Bool then :i8
858
+ when :String, :Object, :Array, :Hash then :value # Ruby objects stored as VALUE
859
+ when Hash
860
+ # Reference to another NativeClass (stored as VALUE)
861
+ :value
862
+ else
863
+ # Check if it's an embedded NativeClass
864
+ if field_type.is_a?(Symbol) && reg.key?(field_type)
865
+ :embedded_native_class
866
+ else
867
+ :value
868
+ end
869
+ end
870
+ end
871
+
872
+ # Check if a field is a NativeClass reference (not embedded)
873
+ def native_class_reference?(field_name, registry = nil)
874
+ all = all_fields(registry || @native_class_registry || {})
875
+ field_type = all[field_name.to_sym]
876
+ field_type.is_a?(Hash) && field_type.key?(:ref)
877
+ end
878
+
879
+ # Get the referenced NativeClass name for a reference field
880
+ def referenced_class_name(field_name, registry = nil)
881
+ all = all_fields(registry || @native_class_registry || {})
882
+ field_type = all[field_name.to_sym]
883
+ field_type.is_a?(Hash) ? field_type[:ref] : nil
884
+ end
885
+
886
+ # Check if this class has any Ruby object fields (need GC marking)
887
+ def has_ruby_object_fields?(registry = nil)
888
+ reg = registry || @native_class_registry || {}
889
+ all_fields(reg).values.any? do |field_type|
890
+ RUBY_OBJECT_TYPES.include?(field_type) || field_type.is_a?(Hash)
891
+ end
892
+ end
893
+
894
+ # Get all Ruby object field names (for GC marking)
895
+ def ruby_object_field_names(registry = nil)
896
+ reg = registry || @native_class_registry || {}
897
+ all_fields(reg).select do |_, field_type|
898
+ RUBY_OBJECT_TYPES.include?(field_type) || field_type.is_a?(Hash)
899
+ end.keys
900
+ end
901
+
902
+ # Wren-style method lookup: walk up superclass chain
903
+ # @param method_name [Symbol] Method name to look up
904
+ # @param registry [Hash{Symbol => NativeClassType}] Registry of all native classes
905
+ # @return [NativeMethodType, nil] Method signature if found
906
+ def lookup_method(method_name, registry)
907
+ method_name = method_name.to_sym
908
+ return @methods[method_name] if @methods.key?(method_name)
909
+ return nil unless @superclass
910
+
911
+ parent = registry[@superclass]
912
+ parent&.lookup_method(method_name, registry)
913
+ end
914
+
915
+ # Find which class implements a method (for static dispatch)
916
+ # @param method_name [Symbol] Method name
917
+ # @param registry [Hash{Symbol => NativeClassType}] Registry of all native classes
918
+ # @return [NativeClassType, nil] The class that implements the method
919
+ def find_method_owner(method_name, registry)
920
+ method_name = method_name.to_sym
921
+ return self if @methods.key?(method_name)
922
+ return nil unless @superclass
923
+
924
+ parent = registry[@superclass]
925
+ parent&.find_method_owner(method_name, registry)
926
+ end
927
+
928
+ # Get all fields including inherited ones (superclass fields come first)
929
+ # @param registry [Hash{Symbol => NativeClassType}] Registry of all native classes
930
+ # @return [Hash{Symbol => Symbol}] All fields in memory layout order
931
+ def all_fields(registry)
932
+ inherited = if @superclass
933
+ parent = registry[@superclass]
934
+ parent ? parent.all_fields(registry) : {}
935
+ else
936
+ {}
937
+ end
938
+ inherited.merge(@fields)
939
+ end
940
+
941
+ # Check if this class has any methods (own or inherited)
942
+ def has_methods?(registry)
943
+ return true unless @methods.empty?
944
+ return false unless @superclass
945
+
946
+ parent = registry[@superclass]
947
+ parent&.has_methods?(registry) || false
948
+ end
949
+
950
+ # Check if a field type is an embedded NativeClass
951
+ def embedded_native_class?(field_name, registry = nil)
952
+ reg = registry || @native_class_registry || {}
953
+ field_type = fields[field_name.to_sym]
954
+ return false unless field_type.is_a?(Symbol)
955
+ return false if ALLOWED_PRIMITIVE_TYPES.include?(field_type)
956
+ reg.key?(field_type)
957
+ end
958
+
959
+ # Get the embedded NativeClassType for a field
960
+ def embedded_class_type(field_name, registry = nil)
961
+ reg = registry || @native_class_registry || {}
962
+ field_type = fields[field_name.to_sym]
963
+ return nil unless field_type.is_a?(Symbol)
964
+ reg[field_type]
965
+ end
966
+
967
+ private
968
+
969
+ def validate_fields!
970
+ fields.each do |field_name, field_type|
971
+ next if ALLOWED_PRIMITIVE_TYPES.include?(field_type)
972
+
973
+ # Check if it's an embedded NativeClass type (validated later when registry is available)
974
+ next if field_type.is_a?(Symbol)
975
+
976
+ raise ArgumentError,
977
+ "NativeClass field '#{field_name}' has invalid type #{field_type}. " \
978
+ "Allowed types: #{ALLOWED_PRIMITIVE_TYPES.join(', ')} or another NativeClass"
979
+ end
980
+ end
981
+ end
982
+
983
+ # Method signature for NativeClass methods
984
+ class NativeMethodType
985
+ attr_reader :param_types, :return_type, :param_names
986
+
987
+ # Allowed types for parameters and return values
988
+ # - :Int64, :Float64 - primitive unboxed types
989
+ # - Symbol (class name) - reference to another NativeClass
990
+ # - :Self - the class itself (for return type)
991
+ # - :Void - no return value (returns nil)
992
+
993
+ # @param param_types [Array<Symbol>] Parameter types
994
+ # @param return_type [Symbol] Return type
995
+ # @param param_names [Array<Symbol>] Optional parameter names
996
+ def initialize(param_types, return_type, param_names: [])
997
+ @param_types = param_types
998
+ @return_type = return_type
999
+ @param_names = param_names
1000
+ end
1001
+
1002
+ def ==(other)
1003
+ return false unless other.is_a?(NativeMethodType)
1004
+ param_types == other.param_types && return_type == other.return_type
1005
+ end
1006
+
1007
+ def hash
1008
+ [param_types, return_type].hash
1009
+ end
1010
+
1011
+ def to_s
1012
+ params = param_types.map(&:to_s).join(", ")
1013
+ "(#{params}) -> #{return_type}"
1014
+ end
1015
+
1016
+ # Get arity (number of parameters, excluding self)
1017
+ def arity
1018
+ param_types.size
1019
+ end
1020
+
1021
+ # Check if this method returns a value
1022
+ def returns_value?
1023
+ return_type != :Void
1024
+ end
1025
+
1026
+ # Convert return type to internal type representation
1027
+ def return_type_as_internal
1028
+ case return_type
1029
+ when :Int64 then ClassInstance.new(:Integer)
1030
+ when :Float64 then ClassInstance.new(:Float)
1031
+ when :Void then NIL
1032
+ else ClassInstance.new(return_type)
1033
+ end
1034
+ end
1035
+ end
1036
+
1037
+ # C function type - represents an external C function callable via @cfunc
1038
+ # Used for direct C function calls without rb_funcallv overhead
1039
+ # Example RBS annotation:
1040
+ # # @cfunc "fast_sin" : (Float) -> Float
1041
+ # def self.sin: (Float) -> Float
1042
+ class CFuncType
1043
+ attr_reader :c_func_name, :param_types, :return_type
1044
+
1045
+ # Type mappings from RBS to C/LLVM types
1046
+ C_TYPE_MAP = {
1047
+ Float: :double,
1048
+ Integer: :int64,
1049
+ String: :ptr,
1050
+ Bool: :i1,
1051
+ void: :void
1052
+ }.freeze
1053
+
1054
+ # @param c_func_name [String] The C function name to call
1055
+ # @param param_types [Array<Symbol>] Parameter types (:Float, :Integer, etc.)
1056
+ # @param return_type [Symbol] Return type
1057
+ def initialize(c_func_name, param_types, return_type)
1058
+ @c_func_name = c_func_name
1059
+ @param_types = param_types
1060
+ @return_type = return_type
1061
+ end
1062
+
1063
+ def ==(other)
1064
+ return false unless other.is_a?(CFuncType)
1065
+
1066
+ c_func_name == other.c_func_name &&
1067
+ param_types == other.param_types &&
1068
+ return_type == other.return_type
1069
+ end
1070
+
1071
+ def hash
1072
+ [c_func_name, param_types, return_type].hash
1073
+ end
1074
+
1075
+ def to_s
1076
+ "CFuncType(#{c_func_name}: (#{param_types.join(", ")}) -> #{return_type})"
1077
+ end
1078
+
1079
+ def llvm_param_types
1080
+ param_types.map { |t| C_TYPE_MAP[t] || :value }
1081
+ end
1082
+
1083
+ def llvm_return_type
1084
+ C_TYPE_MAP[return_type] || :value
1085
+ end
1086
+ end
1087
+
1088
+ # Method signature for extern class methods
1089
+ # Used for both constructors (returning opaque pointer) and instance methods
1090
+ class ExternMethodType
1091
+ attr_reader :c_func_name, :param_types, :return_type, :is_constructor
1092
+
1093
+ # Type mappings from RBS to C/LLVM types (same as CFuncType)
1094
+ C_TYPE_MAP = CFuncType::C_TYPE_MAP
1095
+
1096
+ # @param c_func_name [String] The C function name to call
1097
+ # @param param_types [Array<Symbol>] Parameter types (:Float, :Integer, :String, :ptr, etc.)
1098
+ # @param return_type [Symbol] Return type (:ptr for constructor, or other types)
1099
+ # @param is_constructor [Boolean] True if this is a constructor method (self.xxx)
1100
+ def initialize(c_func_name, param_types, return_type, is_constructor: false)
1101
+ @c_func_name = c_func_name
1102
+ @param_types = param_types
1103
+ @return_type = return_type
1104
+ @is_constructor = is_constructor
1105
+ end
1106
+
1107
+ def ==(other)
1108
+ return false unless other.is_a?(ExternMethodType)
1109
+
1110
+ c_func_name == other.c_func_name &&
1111
+ param_types == other.param_types &&
1112
+ return_type == other.return_type &&
1113
+ is_constructor == other.is_constructor
1114
+ end
1115
+
1116
+ def hash
1117
+ [c_func_name, param_types, return_type, is_constructor].hash
1118
+ end
1119
+
1120
+ def to_s
1121
+ kind = is_constructor ? "constructor" : "method"
1122
+ "ExternMethod[#{kind}](#{c_func_name}: (#{param_types.join(", ")}) -> #{return_type})"
1123
+ end
1124
+
1125
+ def llvm_param_types
1126
+ param_types.map { |t| C_TYPE_MAP[t] || :value }
1127
+ end
1128
+
1129
+ def llvm_return_type
1130
+ C_TYPE_MAP[return_type] || :value
1131
+ end
1132
+
1133
+ # Convert return type to internal type representation
1134
+ def return_type_as_internal
1135
+ case return_type
1136
+ when :ptr then UNTYPED # Opaque pointer, handled specially
1137
+ when :Float then FLOAT
1138
+ when :Integer then INTEGER
1139
+ when :String then STRING
1140
+ when :Bool then BOOL
1141
+ when :void then NIL
1142
+ else UNTYPED
1143
+ end
1144
+ end
1145
+ end
1146
+
1147
+ # ExternClass type - wraps pointer to external C struct
1148
+ # Marked with @native extern annotation in RBS
1149
+ # Only holds void* pointer, no field definitions
1150
+ # Requires @ffi annotation for library linking
1151
+ #
1152
+ # Example:
1153
+ # # @ffi "libsqlite3"
1154
+ # # @native extern
1155
+ # class SQLiteDB
1156
+ # def self.open: (String path) -> SQLiteDB
1157
+ # def execute: (String sql) -> Array
1158
+ # def close: () -> void
1159
+ # end
1160
+ class ExternClassType < Type
1161
+ attr_reader :name, :ffi_library, :methods
1162
+
1163
+ # @param name [Symbol] Class name
1164
+ # @param ffi_library [String] Library name to link (e.g., "libsqlite3")
1165
+ # @param methods [Hash{Symbol => ExternMethodType}] Method name -> signature
1166
+ def initialize(name, ffi_library, methods = {})
1167
+ @name = name.to_sym
1168
+ @ffi_library = ffi_library
1169
+ @methods = methods
1170
+ end
1171
+
1172
+ def ==(other)
1173
+ return false unless other.is_a?(ExternClassType)
1174
+ name == other.name && ffi_library == other.ffi_library
1175
+ end
1176
+
1177
+ def hash
1178
+ [name, ffi_library].hash
1179
+ end
1180
+
1181
+ def to_s
1182
+ "ExternClass[#{name} from #{ffi_library}]"
1183
+ end
1184
+
1185
+ # Look up a method by name
1186
+ def lookup_method(method_name)
1187
+ @methods[method_name.to_sym]
1188
+ end
1189
+
1190
+ # Check if a method is a constructor (singleton method that returns Self)
1191
+ def constructor?(method_name)
1192
+ method_sig = @methods[method_name.to_sym]
1193
+ method_sig&.is_constructor || false
1194
+ end
1195
+ end
1196
+
1197
+ # SIMDClass type - fixed-size vector of Float64 values
1198
+ # Marked with @simd annotation in RBS
1199
+ # Supports element-wise arithmetic via LLVM vector operations
1200
+ #
1201
+ # Example:
1202
+ # # @simd
1203
+ # class Vector4
1204
+ # @x: Float
1205
+ # @y: Float
1206
+ # @z: Float
1207
+ # @w: Float
1208
+ #
1209
+ # def add: (Vector4) -> Vector4
1210
+ # def dot: (Vector4) -> Float
1211
+ # end
1212
+ class SIMDClassType < Type
1213
+ attr_reader :name, :field_names, :methods, :vector_width
1214
+
1215
+ # Allowed SIMD widths (must be 2, 3, 4, 8, or 16)
1216
+ ALLOWED_WIDTHS = [2, 3, 4, 8, 16].freeze
1217
+
1218
+ # @param name [Symbol] Class name
1219
+ # @param field_names [Array<Symbol>] Field names in order (all Float64)
1220
+ # @param methods [Hash{Symbol => NativeMethodType}] Method name -> signature
1221
+ def initialize(name, field_names, methods = {})
1222
+ @name = name.to_sym
1223
+ @field_names = field_names.map(&:to_sym)
1224
+ @methods = methods
1225
+ @vector_width = field_names.size
1226
+ validate!
1227
+ end
1228
+
1229
+ def ==(other)
1230
+ return false unless other.is_a?(SIMDClassType)
1231
+ name == other.name && field_names == other.field_names
1232
+ end
1233
+
1234
+ def hash
1235
+ [name, field_names].hash
1236
+ end
1237
+
1238
+ def to_s
1239
+ "SIMDClass[#{name}]<#{vector_width} x double>"
1240
+ end
1241
+
1242
+ # Get field index by name
1243
+ def field_index(field_name)
1244
+ @field_names.index(field_name.to_sym)
1245
+ end
1246
+
1247
+ # Check if a name is a field
1248
+ def field?(field_name)
1249
+ @field_names.include?(field_name.to_sym)
1250
+ end
1251
+
1252
+ # Get LLVM vector width (padded to power of 2)
1253
+ # Vector3 uses <4 x double> internally
1254
+ def llvm_vector_width
1255
+ return 2 if @vector_width <= 2
1256
+ return 4 if @vector_width <= 4
1257
+ return 8 if @vector_width <= 8
1258
+ 16
1259
+ end
1260
+
1261
+ # Look up a method by name
1262
+ def lookup_method(method_name)
1263
+ @methods[method_name.to_sym]
1264
+ end
1265
+
1266
+ private
1267
+
1268
+ def validate!
1269
+ unless ALLOWED_WIDTHS.include?(@vector_width)
1270
+ raise ArgumentError,
1271
+ "SIMDClass '#{@name}' must have #{ALLOWED_WIDTHS.join('/')} Float fields, got #{@vector_width}"
1272
+ end
1273
+ end
1274
+ end
1275
+
1276
+ # NativeModule type - module with methods but no instance state
1277
+ # Marked with @native annotation in RBS
1278
+ # Can be included in classes to mix in methods
1279
+ class NativeModuleType < Type
1280
+ attr_reader :name, :methods
1281
+
1282
+ # @param name [Symbol] Module name
1283
+ # @param methods [Hash{Symbol => NativeMethodType}] Method name -> signature
1284
+ def initialize(name, methods = {})
1285
+ @name = name.to_sym
1286
+ @methods = methods
1287
+ end
1288
+
1289
+ # Look up a method by name
1290
+ def lookup_method(method_name)
1291
+ @methods[method_name.to_sym]
1292
+ end
1293
+
1294
+ def ==(other)
1295
+ return false unless other.is_a?(NativeModuleType)
1296
+ name == other.name
1297
+ end
1298
+
1299
+ def hash
1300
+ name.hash
1301
+ end
1302
+
1303
+ def to_s
1304
+ "NativeModule(#{name})"
1305
+ end
1306
+ end
1307
+
1308
+ # Tuple type [A, B, C]
1309
+ class Tuple < Type
1310
+ attr_reader :element_types
1311
+
1312
+ def initialize(element_types)
1313
+ @element_types = element_types
1314
+ end
1315
+
1316
+ def ==(other)
1317
+ return false unless other.is_a?(Tuple)
1318
+ element_types == other.element_types
1319
+ end
1320
+
1321
+ def hash
1322
+ element_types.hash
1323
+ end
1324
+
1325
+ def to_s
1326
+ "[#{element_types.map(&:to_s).join(", ")}]"
1327
+ end
1328
+ end
1329
+
1330
+ # Literal type (specific value)
1331
+ class Literal < Type
1332
+ attr_reader :value
1333
+
1334
+ def initialize(value)
1335
+ @value = value
1336
+ end
1337
+
1338
+ def ==(other)
1339
+ return false unless other.is_a?(Literal)
1340
+ value == other.value
1341
+ end
1342
+
1343
+ def hash
1344
+ value.hash
1345
+ end
1346
+
1347
+ def to_s
1348
+ value.inspect
1349
+ end
1350
+
1351
+ def subtype_of?(other)
1352
+ return true if super
1353
+ return false unless other.is_a?(ClassInstance)
1354
+
1355
+ case value
1356
+ when Integer then other.name == :Integer
1357
+ when Float then other.name == :Float
1358
+ when String then other.name == :String
1359
+ when Symbol then other.name == :Symbol
1360
+ when true, false then other.name == :TrueClass || other.name == :FalseClass
1361
+ else false
1362
+ end
1363
+ end
1364
+ end
1365
+
1366
+ # Helper methods
1367
+ module_function
1368
+
1369
+ UNTYPED = Untyped.new.freeze
1370
+ NIL = NilType.new.freeze
1371
+ BOOL = BoolType.new.freeze
1372
+ BOTTOM = Bottom.new.freeze
1373
+
1374
+ INTEGER = ClassInstance.new(:Integer).freeze
1375
+ FLOAT = ClassInstance.new(:Float).freeze
1376
+ STRING = ClassInstance.new(:String).freeze
1377
+ SYMBOL = ClassInstance.new(:Symbol).freeze
1378
+ REGEXP = ClassInstance.new(:Regexp).freeze
1379
+ TRUE_CLASS = ClassInstance.new(:TrueClass).freeze
1380
+ FALSE_CLASS = ClassInstance.new(:FalseClass).freeze
1381
+ FIBER = ClassInstance.new(:Fiber).freeze
1382
+ THREAD = ClassInstance.new(:Thread).freeze
1383
+ MUTEX = ClassInstance.new(:Mutex).freeze
1384
+ QUEUE = ClassInstance.new(:Queue).freeze
1385
+ CONDITION_VARIABLE = ClassInstance.new(:ConditionVariable).freeze
1386
+ SIZED_QUEUE = ClassInstance.new(:SizedQueue).freeze
1387
+ RACTOR = ClassInstance.new(:Ractor).freeze
1388
+ RACTOR_PORT = ClassInstance.new(:"Ractor::Port").freeze
1389
+
1390
+ # Additional standard library types
1391
+ TIME = ClassInstance.new(:Time).freeze
1392
+ MATCH_DATA = ClassInstance.new(:MatchData).freeze
1393
+ RANGE = ClassInstance.new(:Range).freeze
1394
+
1395
+ # Native buffer types for efficient I/O
1396
+ BYTEBUFFER = ByteBufferType.new.freeze
1397
+ BYTESLICE = ByteSliceType.new.freeze
1398
+ STRINGBUFFER = StringBufferType.new.freeze
1399
+ NATIVESTRING = NativeStringType.new.freeze
1400
+
1401
+ # Generic slice types
1402
+ SLICE_INT64 = SliceType.new(:Int64).freeze
1403
+ SLICE_FLOAT64 = SliceType.new(:Float64).freeze
1404
+
1405
+ def array(element_type)
1406
+ ClassInstance.new(:Array, [element_type])
1407
+ end
1408
+
1409
+ def hash_type(key_type, value_type)
1410
+ ClassInstance.new(:Hash, [key_type, value_type])
1411
+ end
1412
+
1413
+ def union(*types)
1414
+ flat_types = types.flat_map { |t| t.is_a?(Union) ? t.types : [t] }
1415
+ flat_types = flat_types.reject { |t| t.is_a?(Bottom) }
1416
+ return BOTTOM if flat_types.empty?
1417
+ return flat_types.first if flat_types.size == 1
1418
+ Union.new(flat_types)
1419
+ end
1420
+
1421
+ def optional(type)
1422
+ union(type, NIL)
1423
+ end
1424
+
1425
+ def native_array(element_type)
1426
+ NativeArrayType.new(element_type)
1427
+ end
1428
+
1429
+ # Predefined NativeArray types
1430
+ NATIVE_ARRAY_INT64 = NativeArrayType.new(:Int64).freeze
1431
+ NATIVE_ARRAY_FLOAT64 = NativeArrayType.new(:Float64).freeze
1432
+ end
1433
+ end
1434
+ end