mt-lang 0.3.41 → 0.3.42
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 +4 -4
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/bindings/bindgen/declaration.rb +17 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +44 -5
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +7 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +22 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +9 -0
- data/lib/milk_tea/bindings/vendored_box3d.rb +77 -0
- data/lib/milk_tea/bindings.rb +1 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +21 -5
- data/lib/milk_tea/core/c_backend/feature_detection.rb +1 -1
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +8 -4
- data/lib/milk_tea/core/c_backend/statements.rb +1 -1
- data/lib/milk_tea/core/c_backend/type_collectors.rb +23 -4
- data/lib/milk_tea/core/lowering/async/async_lowering.rb +2 -6
- data/lib/milk_tea/core/lowering/async/frame_builder.rb +12 -5
- data/lib/milk_tea/core/lowering/async/normalization.rb +26 -5
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +8 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +3 -1
- data/lib/milk_tea/tooling/docs_app.rb +1 -1
- data/std/box3d.mt +784 -0
- data/std/c/box3d.mt +1871 -0
- data/std/c/miniaudio.mt +19 -28
- data/std/c/steamworks.mt +2 -5
- data/std/miniaudio.mt +9 -12
- data/std/steamworks.mt +0 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 927d840392dcd8afb43392ab7ef3f0b620ff267a13107c1f539ad17596364c6c
|
|
4
|
+
data.tar.gz: f9f09a4989def57fbb058b3cc1ceeea62a23d1bfcec0c6bfe030c58e4ea26e8e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a7d0a57ff106227cc3e59d3ac1b425ad8cc0deecf4f0f9016f09984074cd624f43ddc7e178935c4af31a472cdd79eff6275c18a410030d678c0e1a0567666343
|
|
7
|
+
data.tar.gz: 53edd11a322c1145dbab6d63209dd321e720a22c0f269b08f84be31638fac77f7bb092aa5920611cb12603905eb2260612f49e325d0644ebc35bb95295b134c1
|
data/lib/milk_tea/base.rb
CHANGED
|
@@ -280,11 +280,28 @@ module MilkTea
|
|
|
280
280
|
qual_type = node.dig("type", "qualType")
|
|
281
281
|
if macro_probe_declaration?(node) && qual_type.to_s.include?("typeof")
|
|
282
282
|
node.dig("type", "desugaredQualType") || qual_type
|
|
283
|
+
elsif constant_typedef_alias_aggregate?(node)
|
|
284
|
+
node.dig("type", "desugaredQualType") || qual_type
|
|
283
285
|
else
|
|
284
286
|
qual_type
|
|
285
287
|
end
|
|
286
288
|
end
|
|
287
289
|
|
|
290
|
+
# A constant whose spelled type is a typedef alias that resolves to an
|
|
291
|
+
# aggregate (e.g. `typedef b3Vec3 b3Pos` followed by
|
|
292
|
+
# `static const b3Pos b3Pos_zero`) must lower against the underlying
|
|
293
|
+
# record, not the alias name, so init-list emission can find the
|
|
294
|
+
# aggregate declaration.
|
|
295
|
+
def constant_typedef_alias_aggregate?(node)
|
|
296
|
+
qual_type = node.dig("type", "qualType").to_s
|
|
297
|
+
spelled = strip_qualifiers(qual_type)
|
|
298
|
+
return false unless spelled.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
299
|
+
return false if @aggregate_declarations.key?(spelled)
|
|
300
|
+
|
|
301
|
+
desugared = strip_qualifiers(node.dig("type", "desugaredQualType").to_s)
|
|
302
|
+
!desugared.empty? && desugared != spelled && desugared.match?(/\A(?:struct|union)\b/)
|
|
303
|
+
end
|
|
304
|
+
|
|
288
305
|
def function_return_type(node)
|
|
289
306
|
qual_type = type_qual_type(node)
|
|
290
307
|
match = qual_type&.match(/\A(.+?)\s*\((?:.*)\)\z/)
|
|
@@ -75,6 +75,11 @@ module MilkTea
|
|
|
75
75
|
lines = [header]
|
|
76
76
|
fields = Array(node["inner"]).select { |child| child["kind"] == "FieldDecl" }
|
|
77
77
|
fields.each do |field|
|
|
78
|
+
anonymous_record = anonymous_record_decl_for_field(field, node)
|
|
79
|
+
if anonymous_record && field_unnamed?(field)
|
|
80
|
+
lines.concat(emit_flattened_anonymous_record(anonymous_record, owner_name: name))
|
|
81
|
+
next
|
|
82
|
+
end
|
|
78
83
|
field_type = aggregate_field_type(field, owner_name: name, aggregate_node: node)
|
|
79
84
|
mt_name = emitted_name(aggregate_field_name(field, aggregate_node: node))
|
|
80
85
|
lines << " #{mt_name}: #{field_type}"
|
|
@@ -82,6 +87,26 @@ module MilkTea
|
|
|
82
87
|
lines
|
|
83
88
|
end
|
|
84
89
|
|
|
90
|
+
# Anonymous union/struct members are flattened into the owning aggregate,
|
|
91
|
+
# matching C semantics where their fields are accessible directly on the
|
|
92
|
+
# enclosing type (e.g. `b3ChildShape.hull`, `b3TreeNode.children`).
|
|
93
|
+
def emit_flattened_anonymous_record(record, owner_name:)
|
|
94
|
+
Array(record["inner"]).select { |child| child["kind"] == "FieldDecl" }.flat_map do |field|
|
|
95
|
+
anonymous_record = anonymous_record_decl_for_field(field, record)
|
|
96
|
+
if anonymous_record && field_unnamed?(field)
|
|
97
|
+
emit_flattened_anonymous_record(anonymous_record, owner_name:)
|
|
98
|
+
else
|
|
99
|
+
field_type = aggregate_field_type(field, owner_name:, aggregate_node: record)
|
|
100
|
+
[" #{emitted_name(field["name"])}: #{field_type}"]
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def field_unnamed?(field)
|
|
106
|
+
name = field["name"]
|
|
107
|
+
name.nil? || name.empty?
|
|
108
|
+
end
|
|
109
|
+
|
|
85
110
|
def bindgen_param_name(name)
|
|
86
111
|
name = emitted_name(name)
|
|
87
112
|
return [name, nil] unless generated_binding_name_conflict?(name)
|
|
@@ -110,6 +135,13 @@ module MilkTea
|
|
|
110
135
|
anonymous_record = anonymous_record_decl_for_field(field, aggregate_node)
|
|
111
136
|
next unless anonymous_record
|
|
112
137
|
|
|
138
|
+
if field_unnamed?(field)
|
|
139
|
+
# Anonymous member: flattened into the parent. Keep walking so
|
|
140
|
+
# named nested records still get synthesized.
|
|
141
|
+
pending << [owner_name, anonymous_record]
|
|
142
|
+
next
|
|
143
|
+
end
|
|
144
|
+
|
|
113
145
|
synthetic_name = synthetic_aggregate_name(owner_name, field, aggregate_node)
|
|
114
146
|
unless @synthetic_declarations.any? { |declaration| declaration[:name] == synthetic_name }
|
|
115
147
|
@synthetic_declarations << { kind: anonymous_record.fetch("tagUsed"), name: synthetic_name, node: anonymous_record }
|
|
@@ -140,12 +172,19 @@ module MilkTea
|
|
|
140
172
|
seen[key] = true
|
|
141
173
|
|
|
142
174
|
Array(aggregate_node["inner"]).select { |child| child["kind"] == "FieldDecl" }.each do |field|
|
|
143
|
-
aggregate_field_type(field, owner_name:, aggregate_node:)
|
|
144
|
-
|
|
145
175
|
anonymous_record = anonymous_record_decl_for_field(field, aggregate_node)
|
|
146
|
-
|
|
176
|
+
if anonymous_record
|
|
177
|
+
if field_unnamed?(field)
|
|
178
|
+
# Flattened anonymous member: walk its fields under the owner.
|
|
179
|
+
pending << [owner_name, anonymous_record]
|
|
180
|
+
next
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
pending << [synthetic_aggregate_name(owner_name, field, aggregate_node), anonymous_record]
|
|
184
|
+
next
|
|
185
|
+
end
|
|
147
186
|
|
|
148
|
-
|
|
187
|
+
aggregate_field_type(field, owner_name:, aggregate_node:)
|
|
149
188
|
end
|
|
150
189
|
end
|
|
151
190
|
end
|
|
@@ -155,7 +194,7 @@ module MilkTea
|
|
|
155
194
|
return override if override
|
|
156
195
|
|
|
157
196
|
anonymous_record = anonymous_record_decl_for_field(field, aggregate_node)
|
|
158
|
-
return synthetic_aggregate_name(owner_name, field, aggregate_node) if anonymous_record
|
|
197
|
+
return synthetic_aggregate_name(owner_name, field, aggregate_node) if anonymous_record && !field_unnamed?(field)
|
|
159
198
|
|
|
160
199
|
map_type_node(field, context: "field #{owner_name}.#{field["name"]}")
|
|
161
200
|
end
|
|
@@ -60,6 +60,13 @@ module MilkTea
|
|
|
60
60
|
raw_module_name: "std.c.box2d",
|
|
61
61
|
policy_path: root.join("bindings/imported/box2d.binding.json"),
|
|
62
62
|
),
|
|
63
|
+
Binding.new(
|
|
64
|
+
name: "box3d",
|
|
65
|
+
module_name: "std.box3d",
|
|
66
|
+
binding_path: root.join("std/box3d.mt"),
|
|
67
|
+
raw_module_name: "std.c.box3d",
|
|
68
|
+
policy_path: root.join("bindings/imported/box3d.binding.json"),
|
|
69
|
+
),
|
|
63
70
|
Binding.new(
|
|
64
71
|
name: "cjson",
|
|
65
72
|
module_name: "std.cjson",
|
|
@@ -11,6 +11,8 @@ module MilkTea
|
|
|
11
11
|
vendored_glfw_library = vendored_glfw.library(root:)
|
|
12
12
|
vendored_box2d = MilkTea::VendoredBox2D
|
|
13
13
|
vendored_box2d_library = vendored_box2d.library(root:)
|
|
14
|
+
vendored_box3d = MilkTea::VendoredBox3D
|
|
15
|
+
vendored_box3d_library = vendored_box3d.library(root:)
|
|
14
16
|
vendored_cjson = MilkTea::VendoredCJSON
|
|
15
17
|
vendored_cjson_library = vendored_cjson.library(root:)
|
|
16
18
|
vendored_flecs = MilkTea::VendoredFlecs
|
|
@@ -960,6 +962,26 @@ module MilkTea
|
|
|
960
962
|
vendored_box2d.header_root(root:).join("box2d.h").to_s,
|
|
961
963
|
],
|
|
962
964
|
),
|
|
965
|
+
Binding.new(
|
|
966
|
+
name: "box3d",
|
|
967
|
+
module_name: "std.c.box3d",
|
|
968
|
+
binding_path: root.join("std/c/box3d.mt"),
|
|
969
|
+
include_directives: ["box3d/box3d.h"],
|
|
970
|
+
link_libraries: ["box3d"],
|
|
971
|
+
vendored_library: vendored_box3d_library,
|
|
972
|
+
clang_args: vendored_box3d.include_flags(root:),
|
|
973
|
+
compiler_flags: vendored_box3d.include_flags(root:),
|
|
974
|
+
tracked_header_paths: [
|
|
975
|
+
vendored_box3d.header_root(root:).join("box3d.h").to_s,
|
|
976
|
+
],
|
|
977
|
+
tracked_header_prefixes: [
|
|
978
|
+
vendored_box3d.header_root(root:).to_s,
|
|
979
|
+
],
|
|
980
|
+
declaration_name_prefixes: ["b3", "B3_"],
|
|
981
|
+
header_candidates: [
|
|
982
|
+
vendored_box3d.header_root(root:).join("box3d.h").to_s,
|
|
983
|
+
],
|
|
984
|
+
),
|
|
963
985
|
Binding.new(
|
|
964
986
|
name: "cjson",
|
|
965
987
|
module_name: "std.c.cjson",
|
|
@@ -195,6 +195,15 @@ module MilkTea
|
|
|
195
195
|
include/box2d/box2d.h
|
|
196
196
|
],
|
|
197
197
|
),
|
|
198
|
+
Source.new(
|
|
199
|
+
name: "box3d",
|
|
200
|
+
checkout_root: data.join("third_party/box3d-upstream"),
|
|
201
|
+
repository_url: "https://github.com/erincatto/box3d.git",
|
|
202
|
+
revision: "30c67b5e6d0a3a66f0f506c69ce9e9e0587e3b7c",
|
|
203
|
+
sentinel_paths: %w[
|
|
204
|
+
include/box3d/box3d.h
|
|
205
|
+
],
|
|
206
|
+
),
|
|
198
207
|
Source.new(
|
|
199
208
|
name: "cjson",
|
|
200
209
|
checkout_root: data.join("third_party/cjson-upstream"),
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "vendored_c_library"
|
|
4
|
+
|
|
5
|
+
module MilkTea
|
|
6
|
+
module VendoredBox3D
|
|
7
|
+
Error = VendoredCLibrary::Error
|
|
8
|
+
|
|
9
|
+
CONFIGURE_ARGS = %w[
|
|
10
|
+
-DCMAKE_BUILD_TYPE=Release
|
|
11
|
+
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
|
12
|
+
-DBUILD_SHARED_LIBS=OFF
|
|
13
|
+
-DBOX3D_SAMPLES=OFF
|
|
14
|
+
-DBOX3D_BENCHMARKS=OFF
|
|
15
|
+
-DBOX3D_DOCS=OFF
|
|
16
|
+
-DBOX3D_PROFILE=OFF
|
|
17
|
+
-DBOX3D_VALIDATE=OFF
|
|
18
|
+
-DBOX3D_UNIT_TESTS=OFF
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
SYSTEM_LINK_FLAGS = %w[
|
|
22
|
+
-lm
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
25
|
+
def self.library(root: MilkTea.root)
|
|
26
|
+
resolved_root = Pathname.new(File.expand_path(root.to_s))
|
|
27
|
+
@libraries ||= {}
|
|
28
|
+
@libraries[resolved_root.to_s] ||= VendoredCLibrary::CMake.new(
|
|
29
|
+
name: "box3d",
|
|
30
|
+
source_root: source_root(root: resolved_root),
|
|
31
|
+
build_root: build_root(root: resolved_root),
|
|
32
|
+
install_root: install_root(root: resolved_root),
|
|
33
|
+
archive_path: archive_path(root: resolved_root),
|
|
34
|
+
include_roots: [include_root(root: resolved_root)],
|
|
35
|
+
configure_args: CONFIGURE_ARGS,
|
|
36
|
+
system_link_flags: SYSTEM_LINK_FLAGS,
|
|
37
|
+
cc_env_var: "BOX3D_CC",
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.source_root(root: MilkTea.root)
|
|
42
|
+
MilkTea.writable_root_for(root).join("third_party/box3d-upstream")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.include_root(root: MilkTea.root)
|
|
46
|
+
source_root(root:).join("include")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.header_root(root: MilkTea.root)
|
|
50
|
+
include_root(root:).join("box3d")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.build_root(root: MilkTea.root)
|
|
54
|
+
MilkTea.writable_root_for(root).join("tmp/vendored-box3d")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.install_root(root: MilkTea.root)
|
|
58
|
+
MilkTea.writable_root_for(root).join("tmp/vendored-box3d-prefix")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.archive_path(root: MilkTea.root)
|
|
62
|
+
install_root(root:).join("lib/libbox3d.a")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.include_flags(root: MilkTea.root)
|
|
66
|
+
library(root:).include_flags
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.link_flags(root: MilkTea.root)
|
|
70
|
+
library(root:).link_flags
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.prepare!(root: MilkTea.root, **kwargs)
|
|
74
|
+
library(root:).prepare!(**kwargs)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/milk_tea/bindings.rb
CHANGED
|
@@ -11,6 +11,7 @@ require_relative "bindings/vendored_raylib"
|
|
|
11
11
|
require_relative "bindings/vendored_sdl3"
|
|
12
12
|
require_relative "bindings/vendored_glfw"
|
|
13
13
|
require_relative "bindings/vendored_box2d"
|
|
14
|
+
require_relative "bindings/vendored_box3d"
|
|
14
15
|
require_relative "bindings/vendored_cjson"
|
|
15
16
|
require_relative "bindings/vendored_flecs"
|
|
16
17
|
require_relative "bindings/vendored_libuv"
|
|
@@ -17,7 +17,7 @@ module MilkTea
|
|
|
17
17
|
if (alias_name = checked_index_alias(expression))
|
|
18
18
|
"(*#{alias_name})"
|
|
19
19
|
else
|
|
20
|
-
"(*#{checked_array_index_helper_name(expression.receiver_type)}(#{
|
|
20
|
+
"(*#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_checked_array_index_argument(expression.receiver)}, #{emit_expression(expression.index)}))"
|
|
21
21
|
end
|
|
22
22
|
when IR::CheckedSpanIndex
|
|
23
23
|
if (alias_name = checked_index_alias(expression))
|
|
@@ -26,7 +26,7 @@ module MilkTea
|
|
|
26
26
|
"(*#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)}))"
|
|
27
27
|
end
|
|
28
28
|
when IR::NullableIndex
|
|
29
|
-
"#{nullable_array_index_helper_name(expression.receiver_type)}(#{
|
|
29
|
+
"#{nullable_array_index_helper_name(expression.receiver_type)}(#{emit_checked_array_index_argument(expression.receiver)}, #{emit_expression(expression.index)})"
|
|
30
30
|
when IR::NullableSpanIndex
|
|
31
31
|
"#{nullable_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
|
|
32
32
|
when IR::Call
|
|
@@ -81,7 +81,7 @@ module MilkTea
|
|
|
81
81
|
case expression.expression
|
|
82
82
|
when IR::CheckedIndex
|
|
83
83
|
alias_name = checked_index_alias(expression.expression)
|
|
84
|
-
alias_name || "#{checked_array_index_helper_name(expression.expression.receiver_type)}(#{
|
|
84
|
+
alias_name || "#{checked_array_index_helper_name(expression.expression.receiver_type)}(#{emit_checked_array_index_argument(expression.expression.receiver)}, #{emit_expression(expression.expression.index)})"
|
|
85
85
|
when IR::CheckedSpanIndex
|
|
86
86
|
alias_name = checked_index_alias(expression.expression)
|
|
87
87
|
alias_name || "#{checked_span_index_helper_name(expression.expression.receiver_type)}(#{emit_expression(expression.expression.receiver)}, #{emit_expression(expression.expression.index)})"
|
|
@@ -344,6 +344,16 @@ module MilkTea
|
|
|
344
344
|
"(void)#{wrap_expression(expression)}"
|
|
345
345
|
end
|
|
346
346
|
|
|
347
|
+
def emit_checked_array_index_argument(receiver)
|
|
348
|
+
if receiver.is_a?(IR::Unary) && receiver.operator == "*"
|
|
349
|
+
emit_expression(receiver.operand)
|
|
350
|
+
elsif c_expression_lvalue?(receiver)
|
|
351
|
+
emit_expression(receiver)
|
|
352
|
+
else
|
|
353
|
+
"&(#{c_type(receiver.type)}[1]){ #{emit_expression(receiver)} }[0]"
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
347
357
|
def emit_address_of_operand(expression)
|
|
348
358
|
return emit_expression(expression.operand) if expression.is_a?(IR::Unary) && expression.operator == "*"
|
|
349
359
|
|
|
@@ -490,8 +500,14 @@ module MilkTea
|
|
|
490
500
|
def emit_cyclic_array_initializer(field_type, value)
|
|
491
501
|
elem_c_name = c_type(array_element_type(field_type))
|
|
492
502
|
elem_count = array_length(field_type)
|
|
493
|
-
|
|
494
|
-
|
|
503
|
+
source_expr = if value.is_a?(IR::ArrayLiteral)
|
|
504
|
+
"&(#{elem_c_name}[#{elem_count}]){ #{value.elements.map { |e| emit_initializer(e) }.join(", ")} }"
|
|
505
|
+
elsif c_expression_lvalue?(value)
|
|
506
|
+
"&(#{emit_expression(value)})"
|
|
507
|
+
else
|
|
508
|
+
"&(#{elem_c_name}[#{elem_count}]){ #{emit_expression(value)} }"
|
|
509
|
+
end
|
|
510
|
+
"((#{elem_c_name}*)memcpy(malloc(#{elem_count} * sizeof(#{elem_c_name})), #{source_expr}, #{elem_count} * sizeof(#{elem_c_name})))"
|
|
495
511
|
end
|
|
496
512
|
|
|
497
513
|
def emit_cyclic_struct_initializer(field_type, value)
|
|
@@ -137,7 +137,7 @@ module MilkTea
|
|
|
137
137
|
|
|
138
138
|
collect_checked_array_index_types.any? || collect_checked_span_index_types.any? ||
|
|
139
139
|
uses_format_helpers? ||
|
|
140
|
-
emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_fatal mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_assign mt_str_buffer_append mt_foreign_str_to_cstr_temp mt_foreign_strs_to_cstrs_temp]) }
|
|
140
|
+
emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_fatal mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_assign mt_str_buffer_append mt_foreign_str_to_cstr_temp mt_foreign_strs_to_cstrs_temp mt_str_concat]) }
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
def uses_mt_fatal_str_helper?
|
|
@@ -51,6 +51,7 @@ module MilkTea
|
|
|
51
51
|
"",
|
|
52
52
|
"static mt_str mt_str_concat(mt_str a, mt_str b) {",
|
|
53
53
|
"#{INDENT}uintptr_t total = a.len + b.len;",
|
|
54
|
+
"#{INDENT}if (total > MT_STR_CONCAT_BUF_SIZE) mt_fatal(\"str concatenation exceeds the scratch buffer budget\");",
|
|
54
55
|
"#{INDENT}if (mt_str_concat_offset + total > MT_STR_CONCAT_BUF_SIZE) {",
|
|
55
56
|
"#{INDENT * 2}mt_str_concat_offset = 0;",
|
|
56
57
|
"#{INDENT}}",
|
|
@@ -297,6 +298,9 @@ module MilkTea
|
|
|
297
298
|
"#{INDENT}for (int t = 0; t < nworkers; t++) {",
|
|
298
299
|
"#{INDENT * 2}uv_thread_join(&threads[t]);",
|
|
299
300
|
"#{INDENT}}",
|
|
301
|
+
"#{INDENT}for (int t = nworkers + 1; t < count; t++) {",
|
|
302
|
+
"#{INDENT * 2}items[t].work(items[t].data);",
|
|
303
|
+
"#{INDENT}}",
|
|
300
304
|
"}",
|
|
301
305
|
]
|
|
302
306
|
end
|
|
@@ -590,11 +594,11 @@ module MilkTea
|
|
|
590
594
|
|
|
591
595
|
def emit_checked_array_index_helper(type)
|
|
592
596
|
helper_name = checked_array_index_helper_name(type)
|
|
593
|
-
params = [c_declaration(type, '
|
|
597
|
+
params = [c_declaration(pointer_to(array_element_type(type)), 'array'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
594
598
|
[
|
|
595
599
|
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
596
600
|
"#{INDENT}if (index >= #{array_length(type)}) mt_fatal(\"array index out of bounds\");",
|
|
597
|
-
"#{INDENT}return &
|
|
601
|
+
"#{INDENT}return &array[index];",
|
|
598
602
|
"}",
|
|
599
603
|
]
|
|
600
604
|
end
|
|
@@ -612,11 +616,11 @@ module MilkTea
|
|
|
612
616
|
|
|
613
617
|
def emit_nullable_array_index_helper(type)
|
|
614
618
|
helper_name = nullable_array_index_helper_name(type)
|
|
615
|
-
params = [c_declaration(type, '
|
|
619
|
+
params = [c_declaration(pointer_to(array_element_type(type)), 'array'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
|
|
616
620
|
[
|
|
617
621
|
"static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
|
|
618
622
|
"#{INDENT}if (index >= #{array_length(type)}) return NULL;",
|
|
619
|
-
"#{INDENT}return &
|
|
623
|
+
"#{INDENT}return &array[index];",
|
|
620
624
|
"}",
|
|
621
625
|
]
|
|
622
626
|
end
|
|
@@ -495,7 +495,7 @@ module MilkTea
|
|
|
495
495
|
def emit_checked_index_pointer(expression)
|
|
496
496
|
case expression
|
|
497
497
|
when IR::CheckedIndex
|
|
498
|
-
"#{checked_array_index_helper_name(expression.receiver_type)}(#{
|
|
498
|
+
"#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_checked_array_index_argument(expression.receiver)}, #{emit_expression(expression.index)})"
|
|
499
499
|
when IR::CheckedSpanIndex
|
|
500
500
|
"#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
|
|
501
501
|
else
|
|
@@ -223,6 +223,26 @@ module MilkTea
|
|
|
223
223
|
soa_types = []
|
|
224
224
|
visited = {}
|
|
225
225
|
|
|
226
|
+
all_emitted_top_level_values.each do |value|
|
|
227
|
+
collect_soa_type(value.type, soa_types, visited)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
@program.structs.each do |struct_decl|
|
|
231
|
+
struct_decl.fields.each do |field|
|
|
232
|
+
collect_soa_type(field.type, soa_types, visited)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
@program.unions.each do |union_decl|
|
|
237
|
+
union_decl.fields.each do |field|
|
|
238
|
+
collect_soa_type(field.type, soa_types, visited)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
each_variant_arm_field_type do |field_type|
|
|
243
|
+
collect_soa_type(field_type, soa_types, visited)
|
|
244
|
+
end
|
|
245
|
+
|
|
226
246
|
emitted_functions.each do |function|
|
|
227
247
|
collect_soa_type(function.return_type, soa_types, visited)
|
|
228
248
|
function.params.each do |param|
|
|
@@ -231,10 +251,9 @@ module MilkTea
|
|
|
231
251
|
collect_soa_from_statements(function.body, soa_types, visited)
|
|
232
252
|
end
|
|
233
253
|
|
|
234
|
-
@program.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
end
|
|
254
|
+
@program.static_asserts.each do |statement|
|
|
255
|
+
collect_soa_type_in_expression(statement.condition, soa_types, visited)
|
|
256
|
+
collect_soa_type_in_expression(statement.message, soa_types, visited)
|
|
238
257
|
end
|
|
239
258
|
|
|
240
259
|
soa_types.uniq
|
|
@@ -636,13 +636,9 @@ module MilkTea
|
|
|
636
636
|
active_defers: active_defers + local_defers,
|
|
637
637
|
loop_flow: nested_loop_flow(loop_flow, local_defers),
|
|
638
638
|
)
|
|
639
|
+
storage_ref = IR::Name.new(name: linkage_name, type: storage_type, pointer: false)
|
|
639
640
|
lowered << IR::IfStmt.new(
|
|
640
|
-
condition:
|
|
641
|
-
operator: "==",
|
|
642
|
-
left: IR::Name.new(name: linkage_name, type: storage_type, pointer: false),
|
|
643
|
-
right: IR::NullLiteral.new(type: storage_type),
|
|
644
|
-
type: @ctx.types.fetch("bool"),
|
|
645
|
-
),
|
|
641
|
+
condition: let_else_failure_condition(storage_ref, storage_type),
|
|
646
642
|
then_body: else_body,
|
|
647
643
|
else_body: nil,
|
|
648
644
|
)
|
|
@@ -421,7 +421,8 @@ module MilkTea
|
|
|
421
421
|
body = [async_frame_cast_declaration(frame_type, async_info)]
|
|
422
422
|
|
|
423
423
|
not_ready_expr = IR::Unary.new(operator: "not", operand: async_frame_field_expression(frame_expr, "ready", @ctx.types.fetch("bool")), type: @ctx.types.fetch("bool"))
|
|
424
|
-
|
|
424
|
+
frame_free_stmt = IR::ExpressionStmt.new(expression: IR::Call.new(callee: "mt_async_free", arguments: [raw_frame_expr], type: @ctx.types.fetch("void")))
|
|
425
|
+
not_ready_return = [frame_free_stmt, IR::ReturnStmt.new(value: nil)]
|
|
425
426
|
|
|
426
427
|
if async_info[:await_fields].any?
|
|
427
428
|
await_release_stmts = []
|
|
@@ -589,11 +590,17 @@ module MilkTea
|
|
|
589
590
|
)
|
|
590
591
|
body << IR::ExpressionStmt.new(expression: ready_assign)
|
|
591
592
|
|
|
592
|
-
|
|
593
|
+
waiter_frame_field = async_frame_field_expression(frame_expr, "waiter_frame", async_info[:void_ptr])
|
|
593
594
|
wake_stmts = [
|
|
595
|
+
IR::LocalDecl.new(
|
|
596
|
+
name: "waiter_frame",
|
|
597
|
+
linkage_name: "__mt_waiter_frame",
|
|
598
|
+
type: async_info[:void_ptr],
|
|
599
|
+
value: waiter_frame_field,
|
|
600
|
+
),
|
|
594
601
|
IR::ExpressionStmt.new(
|
|
595
602
|
expression: IR::Assignment.new(
|
|
596
|
-
target:
|
|
603
|
+
target: waiter_frame_field,
|
|
597
604
|
operator: "=",
|
|
598
605
|
value: IR::NullLiteral.new(type: async_info[:void_ptr]),
|
|
599
606
|
),
|
|
@@ -601,13 +608,13 @@ module MilkTea
|
|
|
601
608
|
IR::ExpressionStmt.new(
|
|
602
609
|
expression: IR::Call.new(
|
|
603
610
|
callee: IR::Name.new(name: async_frame_field_c_name("waiter"), type: async_info[:wake_type], pointer: false),
|
|
604
|
-
arguments: [
|
|
611
|
+
arguments: [IR::Name.new(name: "__mt_waiter_frame", type: async_info[:void_ptr], pointer: false)],
|
|
605
612
|
type: @ctx.types.fetch("void"),
|
|
606
613
|
),
|
|
607
614
|
),
|
|
608
615
|
]
|
|
609
616
|
body << IR::IfStmt.new(
|
|
610
|
-
condition:
|
|
617
|
+
condition: waiter_frame_field,
|
|
611
618
|
then_body: wake_stmts,
|
|
612
619
|
else_body: nil,
|
|
613
620
|
)
|
|
@@ -364,6 +364,17 @@ module MilkTea
|
|
|
364
364
|
end
|
|
365
365
|
end
|
|
366
366
|
|
|
367
|
+
# Emits a type-name prefix that the semantic re-check of generated proc
|
|
368
|
+
# roots can actually resolve: bare names for prelude and current-module
|
|
369
|
+
# types, the qualified module path otherwise.
|
|
370
|
+
def ast_type_ref_base_parts(type)
|
|
371
|
+
return [type.name] if type.module_name.nil?
|
|
372
|
+
return [type.name] if type.module_name == @ctx.module_name
|
|
373
|
+
return [type.name] if %w[std.option std.result].include?(type.module_name)
|
|
374
|
+
|
|
375
|
+
type.module_name.split(".") + [type.name]
|
|
376
|
+
end
|
|
377
|
+
|
|
367
378
|
def ast_type_ref_for(type)
|
|
368
379
|
case type
|
|
369
380
|
when Types::Primitive
|
|
@@ -392,9 +403,20 @@ module MilkTea
|
|
|
392
403
|
when Types::TypeVar
|
|
393
404
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
|
|
394
405
|
when Types::StructInstance
|
|
395
|
-
base_parts = type.module_name ? type.module_name.split(".") + [type.name] : [type.name]
|
|
396
406
|
AST::TypeRef.new(
|
|
397
|
-
name: AST::QualifiedName.new(parts:
|
|
407
|
+
name: AST::QualifiedName.new(parts: ast_type_ref_base_parts(type)),
|
|
408
|
+
arguments: type.arguments.map do |argument|
|
|
409
|
+
if argument.is_a?(Types::LiteralTypeArg)
|
|
410
|
+
AST::TypeArgument.new(value: AST::IntegerLiteral.new(lexeme: argument.value.to_s, value: argument.value))
|
|
411
|
+
else
|
|
412
|
+
AST::TypeArgument.new(value: ast_type_ref_for(argument))
|
|
413
|
+
end
|
|
414
|
+
end,
|
|
415
|
+
nullable: false,
|
|
416
|
+
)
|
|
417
|
+
when Types::VariantInstance
|
|
418
|
+
AST::TypeRef.new(
|
|
419
|
+
name: AST::QualifiedName.new(parts: ast_type_ref_base_parts(type)),
|
|
398
420
|
arguments: type.arguments.map do |argument|
|
|
399
421
|
if argument.is_a?(Types::LiteralTypeArg)
|
|
400
422
|
AST::TypeArgument.new(value: AST::IntegerLiteral.new(lexeme: argument.value.to_s, value: argument.value))
|
|
@@ -404,9 +426,8 @@ module MilkTea
|
|
|
404
426
|
end,
|
|
405
427
|
nullable: false,
|
|
406
428
|
)
|
|
407
|
-
when Types::Struct, Types::Union, Types::Opaque, Types::Enum, Types::Flags
|
|
408
|
-
|
|
409
|
-
AST::TypeRef.new(name: AST::QualifiedName.new(parts: parts), arguments: [], nullable: false)
|
|
429
|
+
when Types::Struct, Types::Union, Types::Opaque, Types::Enum, Types::Flags, Types::Variant
|
|
430
|
+
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ast_type_ref_base_parts(type)), arguments: [], nullable: false)
|
|
410
431
|
when Types::Function
|
|
411
432
|
AST::FunctionType.new(
|
|
412
433
|
params: type.params.each_with_index.map { |param, i| AST::Param.new(name: param.name || "p#{i}", type: ast_type_ref_for(param.type)) },
|
|
@@ -248,7 +248,13 @@ module MilkTea
|
|
|
248
248
|
raise_sema_error("unsupported expression #{expression.class.name}")
|
|
249
249
|
end
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
# Specialized generic-instance bodies share the same AST nodes across
|
|
252
|
+
# substitutions, so a node-id keyed cache cannot be populated there:
|
|
253
|
+
# the last-checked instance would overwrite every other instance's
|
|
254
|
+
# types and leak wrong types to lowering, which reads this cache.
|
|
255
|
+
# Regular functions carry an empty (but non-nil) substitution hash, so
|
|
256
|
+
# only a non-empty hash identifies an instance body.
|
|
257
|
+
@resolved_expr_types[@ctx.ast.node_ids[expression.object_id]] = type unless @current_type_substitutions&.any?
|
|
252
258
|
type
|
|
253
259
|
end
|
|
254
260
|
end
|
|
@@ -961,7 +967,7 @@ module MilkTea
|
|
|
961
967
|
callable_kind = resolution.kind
|
|
962
968
|
callable = resolution.value
|
|
963
969
|
receiver = resolution.receiver
|
|
964
|
-
@resolved_call_kinds[@ctx.ast.node_ids[expression.callee.object_id]] = callable_kind
|
|
970
|
+
@resolved_call_kinds[@ctx.ast.node_ids[expression.callee.object_id]] = callable_kind unless @current_type_substitutions&.any?
|
|
965
971
|
|
|
966
972
|
case callable_kind
|
|
967
973
|
when :function
|
|
@@ -695,7 +695,9 @@ module MilkTea
|
|
|
695
695
|
offset = CompileTime::Layout.offset_of(type, binding.const_value.field_name)
|
|
696
696
|
return unless offset
|
|
697
697
|
|
|
698
|
-
|
|
698
|
+
# Same node-id key collision as resolved_expr_types: instance bodies
|
|
699
|
+
# share AST nodes across substitutions, so never cache their values.
|
|
700
|
+
@const_values[@ctx.ast.node_ids[expression.object_id]] = offset unless @current_type_substitutions&.any?
|
|
699
701
|
end
|
|
700
702
|
|
|
701
703
|
def infer_offsetof_type(type_ref, field_name, scopes: nil)
|
|
@@ -18,7 +18,7 @@ module MilkTea
|
|
|
18
18
|
"Files & I/O" => %w[fs path stdio],
|
|
19
19
|
"System" => %w[ctype errno process time c],
|
|
20
20
|
"Network & HTTP" => %w[net http uri cookie curl],
|
|
21
|
-
"Game & Graphics" => %w[raylib box2d flecs enet cgltf cjson],
|
|
21
|
+
"Game & Graphics" => %w[raylib box2d box3d flecs enet cgltf cjson],
|
|
22
22
|
"Algorithms & AI" => %w[fsm behavior_tree goap],
|
|
23
23
|
"Database & Matching" => %w[sqlite3 pcre2],
|
|
24
24
|
"Utilities" => %w[cli terminal span spatial asset_pack],
|