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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +123 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +11 -0
- data/bin/konpeito +6 -0
- data/konpeito.gemspec +43 -0
- data/lib/konpeito/ast/typed_ast.rb +620 -0
- data/lib/konpeito/ast/visitor.rb +78 -0
- data/lib/konpeito/cache/cache_manager.rb +230 -0
- data/lib/konpeito/cache/dependency_graph.rb +192 -0
- data/lib/konpeito/cache.rb +8 -0
- data/lib/konpeito/cli/base_command.rb +187 -0
- data/lib/konpeito/cli/build_command.rb +220 -0
- data/lib/konpeito/cli/check_command.rb +104 -0
- data/lib/konpeito/cli/config.rb +231 -0
- data/lib/konpeito/cli/deps_command.rb +128 -0
- data/lib/konpeito/cli/doctor_command.rb +340 -0
- data/lib/konpeito/cli/fmt_command.rb +199 -0
- data/lib/konpeito/cli/init_command.rb +312 -0
- data/lib/konpeito/cli/lsp_command.rb +40 -0
- data/lib/konpeito/cli/run_command.rb +150 -0
- data/lib/konpeito/cli/test_command.rb +248 -0
- data/lib/konpeito/cli/watch_command.rb +212 -0
- data/lib/konpeito/cli.rb +301 -0
- data/lib/konpeito/codegen/builtin_methods.rb +229 -0
- data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
- data/lib/konpeito/codegen/debug_info.rb +352 -0
- data/lib/konpeito/codegen/inliner.rb +486 -0
- data/lib/konpeito/codegen/jvm_backend.rb +197 -0
- data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
- data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
- data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
- data/lib/konpeito/codegen/monomorphizer.rb +359 -0
- data/lib/konpeito/codegen/profile_runtime.c +341 -0
- data/lib/konpeito/codegen/profiler.rb +99 -0
- data/lib/konpeito/compiler.rb +592 -0
- data/lib/konpeito/dependency_resolver.rb +296 -0
- data/lib/konpeito/diagnostics/collector.rb +127 -0
- data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
- data/lib/konpeito/diagnostics/renderer.rb +144 -0
- data/lib/konpeito/formatter/formatter.rb +1214 -0
- data/lib/konpeito/hir/builder.rb +7167 -0
- data/lib/konpeito/hir/nodes.rb +2465 -0
- data/lib/konpeito/lsp/document_manager.rb +820 -0
- data/lib/konpeito/lsp/server.rb +183 -0
- data/lib/konpeito/lsp/transport.rb +38 -0
- data/lib/konpeito/parser/prism_adapter.rb +65 -0
- data/lib/konpeito/platform.rb +103 -0
- data/lib/konpeito/profile/report.rb +136 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
- data/lib/konpeito/stdlib/compression/compression.rb +72 -0
- data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
- data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
- data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
- data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
- data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
- data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
- data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
- data/lib/konpeito/stdlib/http/extconf.rb +19 -0
- data/lib/konpeito/stdlib/http/http.rb +125 -0
- data/lib/konpeito/stdlib/http/http.rbs +57 -0
- data/lib/konpeito/stdlib/http/http_native.c +440 -0
- data/lib/konpeito/stdlib/json/extconf.rb +17 -0
- data/lib/konpeito/stdlib/json/json.rb +44 -0
- data/lib/konpeito/stdlib/json/json.rbs +33 -0
- data/lib/konpeito/stdlib/json/json_native.c +286 -0
- data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
- data/lib/konpeito/stdlib/ui/ui.rb +318 -0
- data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
- data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
- data/lib/konpeito/type_checker/inferrer.rb +565 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
- data/lib/konpeito/type_checker/type_resolver.rb +276 -0
- data/lib/konpeito/type_checker/types.rb +1434 -0
- data/lib/konpeito/type_checker/unification.rb +323 -0
- data/lib/konpeito/ui/animation/animated_state.rb +80 -0
- data/lib/konpeito/ui/animation/easing.rb +59 -0
- data/lib/konpeito/ui/animation/value_tween.rb +66 -0
- data/lib/konpeito/ui/app.rb +379 -0
- data/lib/konpeito/ui/box.rb +38 -0
- data/lib/konpeito/ui/castella.rb +70 -0
- data/lib/konpeito/ui/castella_native.rb +76 -0
- data/lib/konpeito/ui/chart/area_chart.rb +305 -0
- data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
- data/lib/konpeito/ui/chart/base_chart.rb +210 -0
- data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
- data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
- data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
- data/lib/konpeito/ui/chart/line_chart.rb +289 -0
- data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
- data/lib/konpeito/ui/chart/scales.rb +77 -0
- data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
- data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
- data/lib/konpeito/ui/column.rb +271 -0
- data/lib/konpeito/ui/core.rb +2199 -0
- data/lib/konpeito/ui/dsl.rb +443 -0
- data/lib/konpeito/ui/frame.rb +171 -0
- data/lib/konpeito/ui/frame_native.rb +494 -0
- data/lib/konpeito/ui/markdown/ast.rb +124 -0
- data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
- data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
- data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
- data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
- data/lib/konpeito/ui/markdown/parser.rb +805 -0
- data/lib/konpeito/ui/markdown/renderer.rb +639 -0
- data/lib/konpeito/ui/markdown/theme.rb +165 -0
- data/lib/konpeito/ui/render_node.rb +260 -0
- data/lib/konpeito/ui/row.rb +207 -0
- data/lib/konpeito/ui/spacer.rb +18 -0
- data/lib/konpeito/ui/style.rb +799 -0
- data/lib/konpeito/ui/theme.rb +563 -0
- data/lib/konpeito/ui/themes/material.rb +35 -0
- data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
- data/lib/konpeito/ui/widgets/button.rb +103 -0
- data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
- data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
- data/lib/konpeito/ui/widgets/container.rb +91 -0
- data/lib/konpeito/ui/widgets/data_table.rb +667 -0
- data/lib/konpeito/ui/widgets/divider.rb +29 -0
- data/lib/konpeito/ui/widgets/image.rb +105 -0
- data/lib/konpeito/ui/widgets/input.rb +485 -0
- data/lib/konpeito/ui/widgets/markdown.rb +57 -0
- data/lib/konpeito/ui/widgets/modal.rb +163 -0
- data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
- data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
- data/lib/konpeito/ui/widgets/net_image.rb +100 -0
- data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
- data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
- data/lib/konpeito/ui/widgets/slider.rb +133 -0
- data/lib/konpeito/ui/widgets/switch.rb +84 -0
- data/lib/konpeito/ui/widgets/tabs.rb +157 -0
- data/lib/konpeito/ui/widgets/text.rb +110 -0
- data/lib/konpeito/ui/widgets/tree.rb +426 -0
- data/lib/konpeito/version.rb +5 -0
- data/lib/konpeito.rb +109 -0
- data/test_native_array.rb +172 -0
- data/test_native_array_class.rb +197 -0
- data/test_native_class.rb +151 -0
- data/tools/konpeito-asm/build.sh +65 -0
- data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
- 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
|