mt-lang 0.2.22 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -1
  3. data/docs/index.html +2 -1
  4. data/docs/language-design.md +2 -0
  5. data/docs/language-manual.md +16 -1
  6. data/lib/milk_tea/base.rb +1 -1
  7. data/lib/milk_tea/core/ast.rb +2 -2
  8. data/lib/milk_tea/core/c_backend/expressions.rb +11 -0
  9. data/lib/milk_tea/core/c_backend/type_collectors.rb +49 -0
  10. data/lib/milk_tea/core/c_backend/type_declaration.rb +7 -0
  11. data/lib/milk_tea/core/c_backend/type_system.rb +12 -0
  12. data/lib/milk_tea/core/c_backend.rb +8 -0
  13. data/lib/milk_tea/core/compatibility_helpers.rb +4 -0
  14. data/lib/milk_tea/core/compile_time.rb +16 -0
  15. data/lib/milk_tea/core/ir.rb +1 -0
  16. data/lib/milk_tea/core/keywords.rb +1 -1
  17. data/lib/milk_tea/core/lowering/block.rb +48 -16
  18. data/lib/milk_tea/core/lowering/calls.rb +75 -1
  19. data/lib/milk_tea/core/lowering/declarations.rb +7 -0
  20. data/lib/milk_tea/core/lowering/expressions.rb +39 -8
  21. data/lib/milk_tea/core/lowering/functions.rb +1 -1
  22. data/lib/milk_tea/core/lowering/resolve.rb +70 -5
  23. data/lib/milk_tea/core/lowering/utils.rb +26 -0
  24. data/lib/milk_tea/core/module_binder.rb +4 -0
  25. data/lib/milk_tea/core/parser/expressions.rb +11 -3
  26. data/lib/milk_tea/core/parser/statements.rb +2 -2
  27. data/lib/milk_tea/core/parser/types.rb +8 -1
  28. data/lib/milk_tea/core/semantic_analyzer/calls.rb +97 -4
  29. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +52 -2
  30. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +7 -0
  31. data/lib/milk_tea/core/semantic_analyzer/generics.rb +40 -0
  32. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +11 -0
  33. data/lib/milk_tea/core/semantic_analyzer/statements.rb +21 -4
  34. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +23 -0
  35. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +37 -0
  36. data/lib/milk_tea/core/types/layout.rb +7 -0
  37. data/lib/milk_tea/core/types/predicates.rb +44 -3
  38. data/lib/milk_tea/core/types/registry.rb +4 -0
  39. data/lib/milk_tea/core/types/types.rb +42 -0
  40. data/lib/milk_tea/core/types/visitor.rb +2 -0
  41. data/lib/milk_tea/lsp/server/completion.rb +1 -1
  42. data/lib/milk_tea/lsp/server/hover.rb +8 -1
  43. data/std/raylib.mt +14 -14
  44. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2ffe49a95d73789f46660b0bf1ccdd0e8cca26846d19a33352751c93371531a
4
- data.tar.gz: 75be2b9035d9271b691849b98769073d875f3a81aeb656feafab63b5998ca371
3
+ metadata.gz: e7b3cb1fb6603adc1eae75fca276b3cbe83fa5daac424d70dda2386200c541da
4
+ data.tar.gz: ccd1d4f1d2bfb4d19be13053a90445be1c6aedc0785d6e089f43f4ad650e72d9
5
5
  SHA512:
6
- metadata.gz: a52f4fe111b80f9ea19dd24b1df46356a1450134a3bd01fce0abd50d482e4b670ad9462d4884384a09447ab1483f6c49e37175191da86abb8143fc7446109f5a
7
- data.tar.gz: 75be516db3258f9c705fcf6d9f4d3a84995e2fb2c91dddbc8899cf7ac3f79fdc1054b7068567654aef88c29637e5dff136894b1ceed50b4436354525e3298d53
6
+ metadata.gz: 9b378bb8cc0e369b7b9f318821c4c5c287135e4e88c75bef3e26944935b4a3679d128f5e273b3de2851fe16dd3af38065fd74ceba23d334ef67a8f716cb73319
7
+ data.tar.gz: 39c93ea971b8092f9222a1ded6a01512087f232dcb431868227708897cac5defd72ca1b30fa180113a43688aebd5aad2983629b2ad7cc3314d0fb583c3418093
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Milk Tea
1
+ # Milk Tea (Simple Game) Programming Language
2
2
 
3
3
  A statically typed, indentation-based systems language for games.
4
4
  Compiles to readable C. Safe by default, explicit by design.
@@ -360,10 +360,20 @@ Ordinary functions:
360
360
 
361
361
  - Parameters must be typed.
362
362
  - Parameters are non-rebindable.
363
+ - Parameters may include default values using `name: type = default_expr`. All required parameters must appear before defaulted parameters. Default calls are resolved at compile time by inlining the expression at each call site that omits the argument. Defaults are not supported on `external function` or `foreign function`.
363
364
  - Return type defaults to `void` if omitted.
364
365
  - Generic functions are supported.
365
366
  - Generic function and method type parameters may use `implements` constraints.
366
367
 
368
+ ```mt
369
+ function configure(host: str, port: int = 8080, debug: bool = false) -> void:
370
+ pass
371
+
372
+ configure("localhost") # port=8080, debug=false
373
+ configure("localhost", port = 3000) # debug=false
374
+ configure(host = "remote", debug = true) # port=8080
375
+ ```
376
+
367
377
  `const function`:
368
378
 
369
379
  A `const function` is evaluable at compile time. Its body follows the same restrictions as a block-bodied `const`. When called from a compile-time context (`const`, `when`, `inline if`, `inline for`), the call is constant-folded:
@@ -476,6 +486,17 @@ let label = match code:
476
486
  _: "other"
477
487
  ```
478
488
 
489
+ Integer match arms also support range patterns via `start..end`:
490
+
491
+ ```mt
492
+ let tier = match score:
493
+ 0..10: "low"
494
+ 11..50: "mid"
495
+ _: "high"
496
+ ```
497
+
498
+ Range arms are refutable — they do not count toward exhaustiveness — and require `_`.
499
+
479
500
  `match` rules:
480
501
 
481
502
  - Enum and variant matches must be exhaustive unless `_` is present.
@@ -713,6 +734,7 @@ Native type operators:
713
734
  - Vectors (`vecN`/`ivecN`): `+`, `-`, `*` (component-wise) with same-type vectors; `*`, `/` with scalar; unary `-`
714
735
  - Matrices (`matN`): `+`, `-` with same-type matrices; `*`, `/` with scalar; unary `-`
715
736
  - Quaternions (`quat`): `+`, `-`, `*` (component-wise) with same-type quaternions; unary `-`
737
+ - SIMD vectors (`simd[T, N]`): `+`, `-`, `*`, `/` (component-wise same-type or scalar), `%`, `&`, `|`, `^`, `~` (integer lanes), `<<`, `>>` (integer lanes, scalar shift), unary `-`/`~` (integer lanes for `~`)
716
738
 
717
739
  ## 10. Type System
718
740
 
@@ -762,6 +784,7 @@ Type constructors:
762
784
  - `fn(params...) -> R`
763
785
  - `proc(params...) -> R`
764
786
  - `SoA[T, N]` — Structure-of-Arrays: each struct field becomes a separate array of length `N`; access `soa[i].field` reads from column `field` at row `i`
787
+ - `simd[T, N]` — SIMD vector: `N` numeric lanes (128 or 256 bits). Component-wise `+` `-` `*` `/` `%` `&` `|` `^` `~` `<<` `>>`. Lane access via `[i]` (compile-time index). Lowers to GCC/Clang vector extensions for portable x86/ARM vector code.
765
788
  - `dyn[InterfaceName]` — runtime interface value (fat pointer: `{ void* data, void* vtable }`). Constructed via `adapt[Interface](value: ref[T])`. @see §6.
766
789
  - `atomic[T]` — atomic value for lock-free concurrent access. `T` must be a primitive integer or `bool`. Methods: `load() -> T`, `store(value: T)`, `add(value: T) -> T`, `sub(value: T) -> T`, `exchange(value: T) -> T`. All operations use sequential consistency. Lowers to C11 `_Atomic T` with `__atomic_*` builtins.
767
790
  - `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
data/docs/index.html CHANGED
@@ -1272,6 +1272,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)</code></pre>
1272
1272
  <tr><td><code>fn(params...) -&gt; R</code></td><td>Function pointer type</td></tr>
1273
1273
  <tr><td><code>proc(params...) -&gt; R</code></td><td>Closure type (value captures)</td></tr>
1274
1274
  <tr><td><code>SoA[T, N]</code></td><td>Structure-of-Arrays: fields become separate arrays</td></tr>
1275
+ <tr><td><code>simd[T, N]</code></td><td>SIMD vector: N numeric lanes (128/256-bit), GCC vector extensions</td></tr>
1275
1276
  <tr><td><code>dyn[Interface]</code></td><td>Runtime interface value (fat pointer)</td></tr>
1276
1277
  <tr><td><code>atomic[T]</code></td><td>Atomic value for lock-free concurrent access</td></tr>
1277
1278
  <tr><td><code>(T, U)</code></td><td>Tuple type</td></tr>
@@ -2529,7 +2530,7 @@ const TYPES = new Set([
2529
2530
  'bool','byte','short','int','long','ubyte','ushort','uint','ulong',
2530
2531
  'char','ptr_int','ptr_uint','float','double','void',
2531
2532
  'str','cstr','vec2','vec3','vec4','ivec2','ivec3','ivec4',
2532
- 'mat3','mat4','quat','ptr','span','array','SoA',
2533
+ 'mat3','mat4','quat','ptr','span','array','SoA','simd',
2533
2534
  'Option','Result','Task','dyn','ref','const_ptr','own','str_buffer','proc',
2534
2535
  'field_handle','member_handle','callable_handle','attribute_handle',
2535
2536
  'EventError','Subscription','atomic',
@@ -561,6 +561,7 @@ Notes:
561
561
  ```mt
562
562
  array[T, N] # fixed-size array
563
563
  SoA[T, N] # Structure-of-Arrays: flatten struct fields into separate arrays
564
+ simd[T, N] # SIMD vector: N numeric lanes, 128/256-bit, lowers to GCC vector extensions
564
565
  str_buffer[N] # fixed-capacity mutable UTF-8 text buffer
565
566
  ptr[T] # raw pointer
566
567
  span[T] # pointer + length view
@@ -771,6 +772,7 @@ Built-in vector, matrix, and quaternion types support component-wise arithmetic
771
772
  - Vectors (`vecN`/`ivecN`): `+`, `-`, `*` (component-wise same-type); `*`, `/` (scalar); unary `-`
772
773
  - Matrices (`matN`): `+`, `-` (component-wise same-type); `*`, `/` (scalar); unary `-`
773
774
  - Quaternions (`quat`): `+`, `-`, `*` (component-wise same-type); unary `-`
775
+ - SIMD vectors (`simd[T, N]`): `+`, `-`, `*`, `/` (component-wise same-type); `+`, `-`, `*`, `/` (scalar, when scalar matches element type); `%`, `&`, `|`, `^`, `~` (integer element types only); `<<`, `>>` (integer element types, shift-amount is scalar int); unary `-`, `~`
774
776
 
775
777
  ### Casts
776
778
 
@@ -426,6 +426,19 @@ function add(a: int, b: int) -> int:
426
426
  return a + b
427
427
  ```
428
428
 
429
+ Parameters may include default values using `= expr` after the type annotation. All required parameters must appear before any defaulted parameter. The default expression is inlined at each call site that omits the argument — there is zero runtime overhead and no function signature change.
430
+
431
+ ```mt
432
+ function configure(host: str, port: int = 8080, debug: bool = false) -> void:
433
+ pass
434
+
435
+ configure("localhost") # port=8080, debug=false
436
+ configure("localhost", port = 3000) # debug=false
437
+ configure(host = "remote", debug = true) # port=8080
438
+ ```
439
+
440
+ Defaults are not supported on `external function` or `foreign function` declarations.
441
+
429
442
  Rules:
430
443
 
431
444
  - Parameters must be typed.
@@ -566,7 +579,7 @@ Scrutinee types supported:
566
579
 
567
580
  - Enum: arm patterns must be members of that enum.
568
581
  - Variant: arm patterns must be arms of that variant; a payload arm may bind its fields with `as name` or destructure them inline with struct patterns.
569
- - Integer (`byte`, `short`, `int`, `long`, `ubyte`, `ushort`, `uint`, `ulong`, `ptr_int`, `ptr_uint`): arm patterns must be integer literals or char literals.
582
+ - Integer (`byte`, `short`, `int`, `long`, `ubyte`, `ushort`, `uint`, `ulong`, `ptr_int`, `ptr_uint`): arm patterns must be integer literals, char literals, or range patterns (`a..b`).
570
583
  - `str`: arm patterns must be string literals (e.g., `"lex": ...`). Matches via full content comparison using the `equal` builtin.
571
584
  - Tuple: arm patterns must be tuple literal patterns whose elements may be integer literals, char literals, string literals, booleans, or `_` discard.
572
585
 
@@ -960,6 +973,7 @@ Rules:
960
973
  - `ivec2` `ivec3` `ivec4` — integer vectors with `.x` `.y` `.z` `.w` fields
961
974
  - `mat3` `mat4` — column-major matrices; `mat3` has columns `.col0`–`.col2` (each `vec3`), `mat4` has `.col0`–`.col3` (each `vec4`)
962
975
  - `quat` — quaternion with `.x` `.y` `.z` `.w` fields; memory-layout compatible with `vec4`
976
+ - `simd[T, N]` — SIMD vector with `N` lanes of numeric primitive type `T`; total width must be 128 or 256 bits. Supports component-wise arithmetic (`+` `-` `*` `/`), bitwise (`&` `|` `^` `~`), shift (`<<` `>>`), and compound assignment. Lane access via compile-time constant index `[i]`. Lowers to GCC/Clang `__attribute__((__vector_size__))`.
963
977
 
964
978
  Primitive type names are reserved. They cannot be reused for value bindings, parameters, locals, import aliases, or type parameters.
965
979
 
@@ -995,6 +1009,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
995
1009
  - `Option[T]`
996
1010
  - `Result[T, E]`
997
1011
  - `SoA[T, N]` — Structure-of-Arrays: transforms `T`'s fields into separate arrays of length `N`; access as `soa[i].field`
1012
+ - `simd[T, N]` — SIMD vector: fixed-width vector of `N` numeric lanes. Constructed with `simd[T, N](lane0, lane1, ...)`. Supports component-wise `+` `-` `*` `/`, bitwise `&` `|` `^` `~`, shift `<<` `>>`, unary `-` `~`, and compound assignment. Lane access via compile-time constant `[i]`. Total width must be 128 or 256 bits. Lowers to GCC/Clang vector extensions.
998
1013
  - `dyn[InterfaceName]` — runtime interface value (fat pointer: data + vtable). Constructed via `adapt[Interface](value: ref[T])`. @see §3.5.
999
1014
  - `atomic[T]` — atomic value for lock-free concurrent access. `T` must be a primitive integer or `bool`. Methods: `load() -> T`, `store(value: T)`, `add(value: T) -> T`, `sub(value: T) -> T`, `exchange(value: T) -> T`. All operations use sequential consistency. Lowers to C11 `_Atomic T` with `__atomic_*` builtins. `atomic[T]` is zero-initializable and sendable.
1000
1015
  - `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.2.22"
6
+ VERSION = "0.3.0"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -117,8 +117,8 @@ module MilkTea
117
117
  ForeignFunctionDecl = Data.define(:name, :type_params, :params, :return_type, :variadic, :mapping, :visibility, :attributes, :line) do
118
118
  def initialize(name:, type_params:, params:, return_type:, variadic:, mapping:, visibility:, attributes: [], line: nil) = super
119
119
  end
120
- Param = Data.define(:name, :type, :line, :column) do
121
- def initialize(name:, type:, line: nil, column: nil) = super
120
+ Param = Data.define(:name, :type, :line, :column, :default_value) do
121
+ def initialize(name:, type:, line: nil, column: nil, default_value: nil) = super
122
122
  end
123
123
  ForeignParam = Data.define(:name, :type, :mode, :boundary_type)
124
124
  LocalDecl = Data.define(:kind, :name, :type, :value, :else_binding, :else_body, :line, :column, :recovered_else, :destructure_bindings, :destructure_type_name) do
@@ -95,6 +95,8 @@ module MilkTea
95
95
  emit_aggregate_literal(expression)
96
96
  when IR::ArrayLiteral
97
97
  emit_array_compound_literal(expression)
98
+ when IR::SimdLaneWith
99
+ emit_simd_lane_with(expression)
98
100
  when IR::VariantLiteral
99
101
  emit_variant_literal(expression)
100
102
  else
@@ -359,6 +361,15 @@ module MilkTea
359
361
  "(#{c_declaration(expression.type, '')}) #{emit_array_initializer(expression)}"
360
362
  end
361
363
 
364
+ def emit_simd_lane_with(expression)
365
+ simd_type = expression.type
366
+ elem_c = primitive_c_type(simd_type.element_type.name)
367
+ src = wrap_expression(expression.src)
368
+ idx = emit_expression(expression.index)
369
+ val = wrap_expression(expression.value)
370
+ "({ #{c_type(simd_type)} _mt_with = #{src}; ((#{elem_c}*)&_mt_with)[#{idx}] = #{val}; _mt_with; })"
371
+ end
372
+
362
373
  def emit_zero_initializer(type)
363
374
  return "{ 0 }" if type.is_a?(Types::StringView)
364
375
  return "{ 0 }" if array_type?(type)
@@ -270,6 +270,55 @@ module MilkTea
270
270
  end
271
271
  end
272
272
 
273
+ def collect_simd_types
274
+ simd_types = []
275
+ visited = {}
276
+
277
+ emitted_functions.each do |function|
278
+ collect_simd_type(function.return_type, simd_types, visited)
279
+ function.params.each do |param|
280
+ collect_simd_type(param.type, simd_types, visited)
281
+ end
282
+ collect_simd_from_statements(function.body, simd_types, visited)
283
+ end
284
+
285
+ @program.structs.each do |struct_decl|
286
+ struct_decl.fields.each do |field|
287
+ collect_simd_type(field.type, simd_types, visited)
288
+ end
289
+ end
290
+
291
+ simd_types.uniq
292
+ end
293
+
294
+ def collect_simd_from_statements(statements, simd_types, visited)
295
+ statements.each do |stmt|
296
+ case stmt
297
+ when IR::LocalDecl
298
+ collect_simd_type(stmt.type, simd_types, visited)
299
+ when IR::BlockStmt
300
+ collect_simd_from_statements(stmt.body, simd_types, visited)
301
+ when IR::IfStmt
302
+ collect_simd_from_statements(stmt.then_body || [], simd_types, visited)
303
+ collect_simd_from_statements(stmt.else_body || [], simd_types, visited)
304
+ when IR::WhileStmt
305
+ collect_simd_from_statements(stmt.body || [], simd_types, visited)
306
+ when IR::ForStmt
307
+ collect_simd_from_statements(stmt.body || [], simd_types, visited)
308
+ end
309
+ end
310
+ end
311
+
312
+ def collect_simd_type(type, simd_types, visited)
313
+ return unless type
314
+ return if visited[type]
315
+
316
+ if type.is_a?(Types::Simd)
317
+ simd_types << type
318
+ visited[type] = true
319
+ end
320
+ end
321
+
273
322
  def collect_generic_struct_decls
274
323
  collect_generic_struct_types.map do |type|
275
324
  fields = type.fields.map { |field_name, field_type| IR::Field.new(name: field_name, type: field_type) }
@@ -195,6 +195,13 @@ module MilkTea
195
195
  lines
196
196
  end
197
197
 
198
+ def emit_simd_type(type)
199
+ name = simd_type_name(type)
200
+ elem_c = primitive_c_type(type.element_type.name)
201
+ width = type.lane_count * Layout.primitive_layout(type.element_type).first
202
+ ["typedef #{elem_c} #{name} __attribute__((__vector_size__(#{width})));"]
203
+ end
204
+
198
205
  def struct_layout_attributes(struct_decl)
199
206
  attributes = []
200
207
  attributes << "packed" if struct_decl.packed
@@ -162,6 +162,9 @@ module MilkTea
162
162
  when Types::SoA
163
163
  base = soa_type_name(type)
164
164
  pointer ? "#{base}*" : base
165
+ when Types::Simd
166
+ base = simd_type_name(type)
167
+ pointer ? "#{base}*" : base
165
168
  when Types::Tuple
166
169
  base = tuple_type_name(type)
167
170
  pointer ? "#{base}*" : base
@@ -191,6 +194,11 @@ module MilkTea
191
194
  "mt_soa_#{element_name}_#{type.count}"
192
195
  end
193
196
 
197
+ def simd_type_name(type)
198
+ element_name = sanitize_identifier(type.element_type.to_s)
199
+ "mt_simd_#{element_name}x#{type.lane_count}"
200
+ end
201
+
194
202
  def tuple_type_name(type)
195
203
  sanitized = type.element_types.map { |et| sanitize_identifier(et.to_s) }.join("_")
196
204
  base = "mt_tuple_#{sanitized}"
@@ -268,6 +276,10 @@ module MilkTea
268
276
  raise CBackendError, "ref requires at least one type argument" unless [1, 2].include?(type.arguments.length)
269
277
 
270
278
  "#{c_type(type.arguments.length == 1 ? type.arguments.first : type.arguments[1])}*"
279
+ when "array"
280
+ raise CBackendError, "array requires exactly two type arguments" unless type.arguments.length == 2
281
+
282
+ c_type(type.arguments.first)
271
283
  when "str_buffer"
272
284
  raise CBackendError, "str_buffer requires exactly one type argument" unless str_buffer_type?(type)
273
285
 
@@ -169,6 +169,14 @@ module MilkTea
169
169
  lines << ""
170
170
  end
171
171
 
172
+ collect_simd_types.each do |type|
173
+ next if @emitted_simd_type_names&.include?(simd_type_name(type))
174
+ @emitted_simd_type_names ||= Set.new
175
+ @emitted_simd_type_names.add(simd_type_name(type))
176
+ lines.concat(emit_simd_type(type))
177
+ lines << ""
178
+ end
179
+
172
180
  if uses_entrypoint_argv_helpers?
173
181
  lines.concat(emit_entrypoint_argv_helpers)
174
182
  lines << ""
@@ -41,6 +41,10 @@ module MilkTea
41
41
  "compare_exchange" => :atomic_compare_exchange,
42
42
  }.freeze
43
43
 
44
+ SIMD_METHOD_KINDS = {
45
+ "with" => :simd_lane_with,
46
+ }.freeze
47
+
44
48
  def type_ref_from_specialization(expression)
45
49
  case expression.callee
46
50
  when AST::Identifier
@@ -108,6 +108,22 @@ module MilkTea
108
108
  end
109
109
  end
110
110
  end
111
+
112
+ types = if @checker.respond_to?(:types)
113
+ @checker.types
114
+ else
115
+ @checker.instance_variable_get(:@ctx).types
116
+ end
117
+ if (type = types[call_expr.callee.name]) && type.is_a?(Types::Struct)
118
+ fields = {}
119
+ call_expr.arguments.each do |argument|
120
+ val = evaluate_expression(argument.value, scopes:)
121
+ return nil unless val
122
+ fields[argument.name] = val
123
+ end
124
+ next fields
125
+ end
126
+
111
127
  @checker.send(:evaluate_compile_time_const_value, call_expr, scopes:)
112
128
  },
113
129
  )
@@ -76,6 +76,7 @@ module MilkTea
76
76
  AggregateLiteral = Data.define(:type, :fields)
77
77
  AggregateField = Data.define(:name, :value)
78
78
  ArrayLiteral = Data.define(:type, :elements)
79
+ SimdLaneWith = Data.define(:src, :index, :value, :type)
79
80
  VariantDecl = Data.define(:name, :linkage_name, :arms, :source_module) do
80
81
  def initialize(name:, linkage_name:, arms:, source_module: nil) = super
81
82
  end
@@ -90,7 +90,7 @@ module MilkTea
90
90
  ].freeze
91
91
 
92
92
  BUILTIN_TYPE_NAMES = (BUILTIN_PRIMITIVE_NAMES + %w[
93
- ptr const_ptr own ref span array str_buffer atomic Task Option Result SoA
93
+ ptr const_ptr own ref span array str_buffer atomic Task Option Result SoA simd
94
94
  struct_handle field_handle callable_handle attribute_handle member_handle type
95
95
  EventError Subscription
96
96
  ]).freeze
@@ -597,24 +597,56 @@ module MilkTea
597
597
  end
598
598
  lowered.concat(else_body)
599
599
  else
600
- arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
601
- cases = statement.arms.map do |arm|
602
- body = lower_block(
603
- arm.body,
604
- env: local_env,
605
- active_defers: active_defers + local_defers,
606
- return_type:,
607
- loop_flow: arm_loop_flow,
608
- allow_return:,
609
- )
610
- if wildcard_arm_pattern?(arm.pattern)
611
- IR::SwitchDefaultCase.new(body:)
612
- else
613
- value = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
614
- IR::SwitchCase.new(value:, body:)
600
+ has_range_arms = statement.arms.any? { |arm| arm.pattern.is_a?(AST::RangeExpr) }
601
+
602
+ if has_range_arms
603
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
604
+ bool_type = @ctx.types.fetch("bool")
605
+ non_wildcard = statement.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
606
+ wildcard = statement.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
607
+ else_body = if wildcard
608
+ lower_block(wildcard.body, env: local_env, active_defers: active_defers + local_defers, return_type:, loop_flow: arm_loop_flow, allow_return:)
609
+ else
610
+ []
611
+ end
612
+ non_wildcard.reverse_each do |arm|
613
+ arm_body = lower_block(
614
+ arm.body,
615
+ env: local_env,
616
+ active_defers: active_defers + local_defers,
617
+ return_type:,
618
+ loop_flow: arm_loop_flow,
619
+ allow_return:,
620
+ )
621
+ cond = if arm.pattern.is_a?(AST::RangeExpr)
622
+ lower_range_match_condition(arm.pattern, expression, bool_type, env: local_env)
623
+ else
624
+ lit = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
625
+ IR::Binary.new(operator: "==", left: expression, right: lit, type: bool_type)
626
+ end
627
+ else_body = [IR::IfStmt.new(condition: cond, then_body: arm_body, else_body:)]
628
+ end
629
+ lowered.concat(else_body)
630
+ else
631
+ arm_loop_flow = switch_loop_flow(loop_flow, local_defers)
632
+ cases = statement.arms.map do |arm|
633
+ body = lower_block(
634
+ arm.body,
635
+ env: local_env,
636
+ active_defers: active_defers + local_defers,
637
+ return_type:,
638
+ loop_flow: arm_loop_flow,
639
+ allow_return:,
640
+ )
641
+ if wildcard_arm_pattern?(arm.pattern)
642
+ IR::SwitchDefaultCase.new(body:)
643
+ else
644
+ value = lower_expression(arm.pattern, env: local_env, expected_type: scrutinee_type)
645
+ IR::SwitchCase.new(value:, body:)
646
+ end
615
647
  end
648
+ lowered << IR::SwitchStmt.new(expression:, cases:, exhaustive: true)
616
649
  end
617
- lowered << IR::SwitchStmt.new(expression:, cases:, exhaustive: true)
618
650
  end
619
651
  lowered.concat(expression_cleanups.flat_map(&:itself))
620
652
  end
@@ -26,7 +26,9 @@ module MilkTea
26
26
  return lower_foreign_call_inline(expression, callee_binding, env:, type:)
27
27
  end
28
28
 
29
- arguments = lower_call_arguments(expression.arguments, callee_type, env:)
29
+ lowered_arguments = expression.arguments
30
+ lowered_arguments = expand_function_defaults(lowered_arguments, callee_binding) if callee_binding
31
+ arguments = lower_call_arguments(lowered_arguments, callee_type, env:)
30
32
  IR::Call.new(callee: callee_name, arguments:, type:)
31
33
  when :callable_value
32
34
  callee_expression = lower_expression(expression.callee, env:, expected_type: callee_type)
@@ -136,6 +138,8 @@ module MilkTea
136
138
  end
137
139
  when :atomic_load, :atomic_store, :atomic_add, :atomic_sub, :atomic_exchange, :atomic_compare_exchange
138
140
  lower_atomic_method_call(kind, receiver, expression, env:, type:)
141
+ when :simd_lane_with
142
+ lower_simd_lane_with(kind, receiver, expression, env:, type:)
139
143
  when :associated_method
140
144
  arguments = lower_call_arguments(expression.arguments, callee_type, env:)
141
145
  IR::Call.new(callee: callee_name, arguments:, type:)
@@ -218,6 +222,12 @@ module MilkTea
218
222
  lower_contextual_expression(argument.value, env:, expected_type: element_type)
219
223
  end
220
224
  IR::ArrayLiteral.new(type:, elements:)
225
+ when :simd
226
+ element_type = simd_type?(type) ? type.element_type : array_element_type(type)
227
+ elements = expression.arguments.map do |argument|
228
+ lower_contextual_expression(argument.value, env:, expected_type: element_type)
229
+ end
230
+ IR::ArrayLiteral.new(type:, elements:)
221
231
  when :reinterpret
222
232
  argument = expression.arguments.fetch(0)
223
233
  source_type = infer_expression_type(argument.value, env:)
@@ -366,6 +376,49 @@ module MilkTea
366
376
  IR::AddressOf.new(expression: lowered_expression, type: pointer_type)
367
377
  end
368
378
 
379
+ def expand_function_defaults(arguments, binding)
380
+ return arguments unless binding.respond_to?(:ast) && binding.ast.respond_to?(:params)
381
+
382
+ ast_params = binding.ast.params
383
+ has_defaults = ast_params.any? { |p| p.respond_to?(:default_value) && p.default_value }
384
+ return arguments unless has_defaults || arguments.any?(&:name)
385
+
386
+ if arguments.any?(&:name)
387
+ by_position = Array.new(ast_params.length)
388
+ param_names = ast_params.map(&:name)
389
+
390
+ arguments.each do |arg|
391
+ if arg.name
392
+ param_idx = param_names.index(arg.name)
393
+ next unless param_idx
394
+
395
+ by_position[param_idx] = arg
396
+ else
397
+ first_empty = by_position.index(nil)
398
+ by_position[first_empty] = arg if first_empty
399
+ end
400
+ end
401
+
402
+ by_position.each_with_index do |slot, idx|
403
+ next if slot
404
+ next unless ast_params[idx].respond_to?(:default_value) && ast_params[idx].default_value
405
+
406
+ by_position[idx] = AST::Argument.new(name: nil, value: ast_params[idx].default_value)
407
+ end
408
+
409
+ by_position.compact
410
+ else
411
+ expanded = arguments.dup
412
+ while expanded.length < ast_params.length
413
+ ast_param = ast_params[expanded.length]
414
+ break unless ast_param.respond_to?(:default_value) && ast_param.default_value
415
+
416
+ expanded << AST::Argument.new(name: nil, value: ast_param.default_value)
417
+ end
418
+ expanded
419
+ end
420
+ end
421
+
369
422
  def lower_call_arguments(arguments, callee_type, env:)
370
423
  arguments.map.with_index do |argument, index|
371
424
  parameter = index < callee_type.params.length ? callee_type.params[index] : nil
@@ -1517,6 +1570,14 @@ module MilkTea
1517
1570
  end
1518
1571
  end
1519
1572
 
1573
+ def simd_method_kind(receiver_type, name)
1574
+ return unless simd_type?(receiver_type)
1575
+
1576
+ case name
1577
+ when "with" then :simd_lane_with
1578
+ end
1579
+ end
1580
+
1520
1581
  def lower_atomic_method_call(kind, receiver, expression, env:, type:)
1521
1582
  receiver_type = infer_expression_type(receiver, env:)
1522
1583
  elem_type = atomic_element_type(receiver_type)
@@ -1545,6 +1606,19 @@ module MilkTea
1545
1606
  end
1546
1607
  end
1547
1608
 
1609
+ def lower_simd_lane_with(_kind, receiver, expression, env:, type:)
1610
+ receiver_ir = lower_expression(receiver, env:)
1611
+ index_ir = lower_expression(expression.arguments[0].value, env:, expected_type: @ctx.types.fetch("int"))
1612
+ value_ir = lower_contextual_expression(expression.arguments[1].value, env:, expected_type: type.element_type)
1613
+
1614
+ IR::SimdLaneWith.new(
1615
+ src: receiver_ir,
1616
+ index: index_ir,
1617
+ value: value_ir,
1618
+ type:,
1619
+ )
1620
+ end
1621
+
1548
1622
  def lower_event_subscribe_call(expression, env:, runtime:, event_pointer:, type:, once: false)
1549
1623
  stateful = expression.arguments.length == 2
1550
1624
  callee_key = if stateful
@@ -72,6 +72,13 @@ module MilkTea
72
72
  lower_const_value_literal(element_type, element)
73
73
  end
74
74
  IR::ArrayLiteral.new(type:, elements:)
75
+ when Hash
76
+ fields = const_value.map do |name, field_value|
77
+ field_type = type.field(name)
78
+ raise LoweringError, "constant struct field #{name} not found in #{type}" unless field_type
79
+ IR::AggregateField.new(name:, value: lower_const_value_literal(field_type, field_value))
80
+ end
81
+ IR::AggregateLiteral.new(type:, fields:)
75
82
  else
76
83
  IR::IntegerLiteral.new(value: 0, type:)
77
84
  end
@@ -1125,6 +1125,7 @@ module MilkTea
1125
1125
 
1126
1126
  switch_expression = lowered_expression
1127
1127
  string_if_chain = nil
1128
+ range_if_chain = nil
1128
1129
  cases = if scrutinee_type.is_a?(Types::Variant)
1129
1130
  kind_type = @ctx.types.fetch("int")
1130
1131
  switch_expression = IR::Member.new(receiver: lowered_expression, member: "kind", type: kind_type)
@@ -1194,20 +1195,50 @@ module MilkTea
1194
1195
  string_if_chain = else_body
1195
1196
  nil
1196
1197
  else
1197
- expression.arms.map do |arm|
1198
- arm_env = duplicate_env(env)
1199
- value_setup, prepared_value = prepare_expression_for_inline_lowering(arm.value, env: arm_env, expected_type: result_type)
1200
- body = value_setup + [IR::Assignment.new(target: result_ref, operator: "=", value: lower_contextual_expression(prepared_value, env: arm_env, expected_type: result_type))]
1201
- if wildcard_arm_pattern?(arm.pattern)
1202
- IR::SwitchDefaultCase.new(body: body)
1203
- else
1204
- IR::SwitchCase.new(value: lower_expression(arm.pattern, env: arm_env, expected_type: scrutinee_type), body: body)
1198
+ has_range_arms = expression.arms.any? { |arm| arm.pattern.is_a?(AST::RangeExpr) }
1199
+
1200
+ if has_range_arms
1201
+ bool_type = @ctx.types.fetch("bool")
1202
+ non_wildcard = expression.arms.reject { |arm| wildcard_arm_pattern?(arm.pattern) }
1203
+ wildcard = expression.arms.find { |arm| wildcard_arm_pattern?(arm.pattern) }
1204
+ else_body = []
1205
+ if wildcard
1206
+ wildcard_env = duplicate_env(env)
1207
+ wildcard_value_setup, wildcard_prepared = prepare_expression_for_inline_lowering(wildcard.value, env: wildcard_env, expected_type: result_type)
1208
+ else_body = wildcard_value_setup + [IR::Assignment.new(target: result_ref, operator: "=", value: lower_contextual_expression(wildcard_prepared, env: wildcard_env, expected_type: result_type))]
1209
+ end
1210
+ non_wildcard.reverse_each do |arm|
1211
+ arm_env = duplicate_env(env)
1212
+ value_setup, prepared_value = prepare_expression_for_inline_lowering(arm.value, env: arm_env, expected_type: result_type)
1213
+ then_body = value_setup + [IR::Assignment.new(target: result_ref, operator: "=", value: lower_contextual_expression(prepared_value, env: arm_env, expected_type: result_type))]
1214
+ cond = if arm.pattern.is_a?(AST::RangeExpr)
1215
+ lower_range_match_condition(arm.pattern, switch_expression, bool_type, env: arm_env)
1216
+ else
1217
+ lit = lower_expression(arm.pattern, env: arm_env, expected_type: scrutinee_type)
1218
+ IR::Binary.new(operator: "==", left: switch_expression, right: lit, type: bool_type)
1219
+ end
1220
+ else_body = [IR::IfStmt.new(condition: cond, then_body:, else_body:)]
1221
+ end
1222
+ range_if_chain = else_body
1223
+ nil
1224
+ else
1225
+ expression.arms.map do |arm|
1226
+ arm_env = duplicate_env(env)
1227
+ value_setup, prepared_value = prepare_expression_for_inline_lowering(arm.value, env: arm_env, expected_type: result_type)
1228
+ body = value_setup + [IR::Assignment.new(target: result_ref, operator: "=", value: lower_contextual_expression(prepared_value, env: arm_env, expected_type: result_type))]
1229
+ if wildcard_arm_pattern?(arm.pattern)
1230
+ IR::SwitchDefaultCase.new(body: body)
1231
+ else
1232
+ IR::SwitchCase.new(value: lower_expression(arm.pattern, env: arm_env, expected_type: scrutinee_type), body: body)
1233
+ end
1205
1234
  end
1206
1235
  end
1207
1236
  end
1208
1237
 
1209
1238
  if string_if_chain
1210
1239
  [setup + string_if_chain, AST::Identifier.new(name: result_name)]
1240
+ elsif range_if_chain
1241
+ [setup + range_if_chain, AST::Identifier.new(name: result_name)]
1211
1242
  else
1212
1243
  [setup + [IR::SwitchStmt.new(expression: switch_expression, cases: cases, exhaustive: true)], AST::Identifier.new(name: result_name)]
1213
1244
  end