mt-lang 0.3.16 → 0.3.20

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/docs/language-design.md +3 -4
  3. data/docs/language-manual.md +3 -3
  4. data/lib/milk_tea/base.rb +1 -1
  5. data/lib/milk_tea/core/{binding_types.rb → bindings.rb} +1 -1
  6. data/lib/milk_tea/core/c_backend/expressions.rb +34 -23
  7. data/lib/milk_tea/core/c_backend/feature_detection.rb +25 -45
  8. data/lib/milk_tea/core/c_backend/type_collectors.rb +18 -30
  9. data/lib/milk_tea/core/c_backend/type_declaration.rb +0 -6
  10. data/lib/milk_tea/core/c_backend.rb +63 -54
  11. data/lib/milk_tea/core/compile_time.rb +279 -74
  12. data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
  13. data/lib/milk_tea/core/{compatibility_helpers.rb → intrinsics.rb} +9 -2
  14. data/lib/milk_tea/core/lexer.rb +55 -48
  15. data/lib/milk_tea/core/lowering/block.rb +9 -0
  16. data/lib/milk_tea/core/lowering/calls.rb +2 -0
  17. data/lib/milk_tea/core/lowering/declarations.rb +1 -1
  18. data/lib/milk_tea/core/lowering/functions.rb +11 -8
  19. data/lib/milk_tea/core/lowering/resolve.rb +10 -3
  20. data/lib/milk_tea/core/lowering/scans.rb +13 -18
  21. data/lib/milk_tea/core/lowering.rb +18 -18
  22. data/lib/milk_tea/core/module_binder.rb +9 -10
  23. data/lib/milk_tea/core/module_loader.rb +64 -57
  24. data/lib/milk_tea/core/module_path_resolver.rb +1 -4
  25. data/lib/milk_tea/core/parser/declarations.rb +43 -19
  26. data/lib/milk_tea/core/parser/expressions.rb +4 -7
  27. data/lib/milk_tea/core/parser/statements.rb +5 -5
  28. data/lib/milk_tea/core/parser.rb +43 -17
  29. data/lib/milk_tea/core/semantic_analyzer/calls.rb +24 -31
  30. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +20 -23
  31. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +117 -85
  32. data/lib/milk_tea/core/semantic_analyzer/statements.rb +19 -41
  33. data/lib/milk_tea/core/semantic_analyzer.rb +57 -38
  34. data/lib/milk_tea/core.rb +6 -2
  35. data/lib/milk_tea/dap/lldb_dap_backend.rb +153 -0
  36. data/lib/milk_tea/dap/server/lldb_backend.rb +1 -1
  37. data/lib/milk_tea/dap.rb +1 -1
  38. data/lib/milk_tea/lsp/server/semantic_tokens.rb +6 -0
  39. data/lib/milk_tea/lsp.rb +1 -7
  40. data/lib/milk_tea/{bindings/cli.rb → tooling/bindgen_cli.rb} +1 -1
  41. data/lib/milk_tea/tooling/cli/commands/bindgen.rb +11 -0
  42. data/lib/milk_tea/tooling/cli/commands/build.rb +37 -0
  43. data/lib/milk_tea/tooling/cli/commands/cache.rb +46 -0
  44. data/lib/milk_tea/tooling/cli/commands/check.rb +116 -0
  45. data/lib/milk_tea/tooling/cli/commands/command_base.rb +8 -0
  46. data/lib/milk_tea/tooling/cli/commands/completions.rb +48 -0
  47. data/lib/milk_tea/tooling/cli/commands/dap.rb +58 -0
  48. data/lib/milk_tea/tooling/cli/commands/debug.rb +77 -0
  49. data/lib/milk_tea/tooling/cli/commands/deps.rb +17 -0
  50. data/lib/milk_tea/tooling/cli/commands/docs.rb +58 -0
  51. data/lib/milk_tea/tooling/cli/commands/emit_c.rb +64 -0
  52. data/lib/milk_tea/tooling/cli/commands/format.rb +199 -0
  53. data/lib/milk_tea/tooling/cli/commands/lex.rb +46 -0
  54. data/lib/milk_tea/tooling/cli/commands/lint.rb +248 -0
  55. data/lib/milk_tea/tooling/cli/commands/lower.rb +50 -0
  56. data/lib/milk_tea/tooling/cli/commands/lsp.rb +43 -0
  57. data/lib/milk_tea/tooling/cli/commands/new.rb +26 -0
  58. data/lib/milk_tea/tooling/cli/commands/parse.rb +51 -0
  59. data/lib/milk_tea/tooling/cli/commands/run.rb +99 -0
  60. data/lib/milk_tea/tooling/cli/commands/snapshot.rb +117 -0
  61. data/lib/milk_tea/tooling/cli/commands/test.rb +557 -0
  62. data/lib/milk_tea/tooling/cli/commands/toolchain.rb +16 -0
  63. data/lib/milk_tea/tooling/cli.rb +101 -1893
  64. data/lib/milk_tea/tooling.rb +1 -1
  65. metadata +29 -9
  66. data/lib/milk_tea/core/compile_time/const_eval.rb +0 -182
  67. data/lib/milk_tea/core/module_loader/errors.rb +0 -23
  68. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -156
  69. /data/lib/milk_tea/core/lowering/{async.rb → async/frame_builder.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5cd3c1ef5e380b8d0c5d4e20f829400d01c6fdfd3e495e8c98050f4af8b59ba
4
- data.tar.gz: 0e083d1780c9d591b996604a8b489726fa0f0c6fb85b8dc03da87b681383d5ca
3
+ metadata.gz: 83f7a9e4ec79d81bd7d2e64ebb469871fecb5e4b3f888c932061d5fa265b7544
4
+ data.tar.gz: c08d508785143d338a6a47adfcedcf605b30e8e9990913a44178344643944178
5
5
  SHA512:
6
- metadata.gz: 44f866e7d6bf6fc4998e0a9054bf898b27ed47ef38149de1881baf1d810e845b94e3d1f3ff304486654ea339329d726a10d969bcff4021943cc98b03bfe3fe01
7
- data.tar.gz: 0fa7237c14aa9e038b4d51ae08a5971430d0e47b97fb19caf619aeb80a5e562de99aae1cf342a11ffea41e3b2268a128454bbf022b3e9182ddd94ff2ff0aff45
6
+ metadata.gz: 95818de5161127d240aacd92b2f8a727e29d1962c11bd9161faa0f0286ac701d2b328352d7ac51bdab23ed2b76a8ccaa7e698490c4bbe5b99edb9452de76cbac
7
+ data.tar.gz: 0d188ebbabd4f175a201ea61c7cde3393a93b9deed991fbb639935b125e565b3b679f16c0fb72c4f96939475d8b6a199362e66920d65e49dd60e6dbc248df372
@@ -521,7 +521,6 @@ The type system must stay simple, explicit, and close to C.
521
521
  ### Primitive types
522
522
 
523
523
  - `bool`
524
- - `byte`
525
524
  - `char`
526
525
  - `byte`, `short`, `int`, `long`
527
526
  - `ubyte`, `ushort`, `uint`, `ulong`
@@ -587,7 +586,7 @@ Notes:
587
586
  - If low-level code needs to validate raw `array[char, N]` storage as text, that conversion belongs in an explicit helper or imported boundary, not as a built-in method family on raw arrays.
588
587
  - Addressable `str_buffer[N]` values also coerce to `span[char]`, so writable foreign text APIs can still accept builders directly when they do not want a second application-facing text abstraction.
589
588
  - `str_buffer[N]` is not an ABI type. Raw bindings still spell writable text as `ptr[char]` or `span[char]`; `str_buffer[N]` is the caller-side text object.
590
- - `str_buffer[N]` has a built-in text surface: `.clear()`, `.assign(str)`, `.append(str)`, `.len()`, `.capacity()`, `.as_str()`, and `.as_cstr()`.
589
+ - `str_buffer[N]` has a built-in text surface: `.clear()`, `.assign(str)`, `.append(str)`, `.assign_format(str)`, `.append_format(str)`, `.len()`, `.capacity()`, `.as_str()`, and `.as_cstr()`.
591
590
  - `.assign(...)` replaces the current contents and traps at runtime if the new text exceeds capacity.
592
591
  - `.append(...)` extends the current contents and traps at runtime if the appended text would exceed capacity.
593
592
  - `.len()` returns the tracked text length, revalidating UTF-8 and rescanning for the trailing NUL if the builder was passed through a writable `span[char]` or `ptr[char]` alias.
@@ -650,7 +649,7 @@ joins the path with underscores (e.g., `module_Rectangle_Edge`).
650
649
 
651
650
  #### Enums
652
651
 
653
- Enums always have an explicit backing type.
652
+ Enums use an integer backing type, defaulting to `int` when omitted; members auto-increment from 0 or the previous explicit value.
654
653
 
655
654
  ```mt
656
655
  enum WeaponKind: ubyte
@@ -661,7 +660,7 @@ enum WeaponKind: ubyte
661
660
 
662
661
  #### Flags
663
662
 
664
- Flags are named bitmasks with a fixed integer backing type.
663
+ Flags are named bitmasks with an integer backing type (defaulting to `int` when omitted).
665
664
 
666
665
  ```mt
667
666
  flags WindowFlags: uint
@@ -1007,7 +1007,7 @@ Rules:
1007
1007
  - `ivec2` `ivec3` `ivec4` — integer vectors with `.x` `.y` `.z` `.w` fields
1008
1008
  - `mat3` `mat4` — column-major matrices; `mat3` has columns `.col0`–`.col2` (each `vec3`), `mat4` has `.col0`–`.col3` (each `vec4`)
1009
1009
  - `quat` — quaternion with `.x` `.y` `.z` `.w` fields; memory-layout compatible with `vec4`
1010
- - `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__))`.
1010
+ - `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 (`+` `-` `*` `/`, and `%` for integer lanes), bitwise (`&` `|` `^` `~`), shift (`<<` `>>`), and compound assignment. Lane access via compile-time constant index `[i]`. Lowers to GCC/Clang `__attribute__((__vector_size__))`.
1011
1011
 
1012
1012
  Primitive type names are reserved. They cannot be reused for value bindings, parameters, locals, import aliases, or type parameters.
1013
1013
 
@@ -1043,7 +1043,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
1043
1043
  - `Option[T]`
1044
1044
  - `Result[T, E]`
1045
1045
  - `SoA[T, N]` — Structure-of-Arrays: transforms `T`'s fields into separate arrays of length `N`; access as `soa[i].field`
1046
- - `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.
1046
+ - `simd[T, N]` — SIMD vector: fixed-width vector of `N` numeric lanes. Constructed with `simd[T, N](lane0, lane1, ...)`. Supports component-wise `+` `-` `*` `/` (and `%` for integer lanes), 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.
1047
1047
  - `dyn[InterfaceName]` — runtime interface value (fat pointer: data + vtable). Constructed via `adapt[Interface](value: ref[T])`. @see §3.5.
1048
1048
  - `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.
1049
1049
  - `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
@@ -1054,7 +1054,7 @@ let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
1054
1054
  - for pointer-like bases (`ptr[T]`, `const_ptr[T]`, `own[T]`, `cstr`, `fn(...)`, `proc(...)`, opaque), `T?` is a nullable pointer with `null` as the absent value
1055
1055
  - for non-pointer value bases (`int`, `bool`, `float`, structs, ...), `T?` is stored inline by value as a tagged optional (a presence flag plus the value); it copies by value with no hidden heap allocation or pointer aliasing
1056
1056
  - `null` expresses absence in any nullable context; the explicit typed `null[...]` form's target must be pointer-like
1057
- - in nullable pointer-like contexts, use `null` instead of `zero[ptr[T]]`
1057
+ - in nullable pointer-like contexts, `zero[ptr[T]]` is rejected; use `null` instead
1058
1058
  - at an FFI boundary (`external` / `foreign function` parameters and returns), only pointer-like `T?` is allowed; a non-pointer value nullable such as `int?` is rejected — use `ptr[T]?` or pass an explicit struct
1059
1059
 
1060
1060
  ### 6.4 Generics
data/lib/milk_tea/base.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "pathname"
4
4
 
5
5
  module MilkTea
6
- VERSION = "0.3.16"
6
+ VERSION = "0.3.20"
7
7
 
8
8
  def self.root
9
9
  @root ||= Pathname.new(File.expand_path("../..", __dir__))
@@ -15,4 +15,4 @@ module MilkTea
15
15
  def each(&block) = @bindings.each(&block)
16
16
  def each_with_object(init, &block) = @bindings.each_with_object(init, &block)
17
17
  end
18
- end
18
+ end
@@ -331,11 +331,10 @@ module MilkTea
331
331
  end
332
332
 
333
333
  def emit_cast_operand(expression)
334
- case expression
335
- when IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::ZeroInit, IR::Member, IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex, IR::Call, IR::AggregateLiteral, IR::ArrayLiteral, IR::ReinterpretExpr, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr, IR::AddressOf, IR::Cast, IR::Unary
336
- emit_expression(expression)
337
- else
334
+ if expression.is_a?(IR::Binary) || expression.is_a?(IR::Conditional)
338
335
  "(#{emit_expression(expression)})"
336
+ else
337
+ emit_expression(expression)
339
338
  end
340
339
  end
341
340
 
@@ -425,28 +424,16 @@ module MilkTea
425
424
 
426
425
  def emit_variant_field_initializer(type, arm_name, field)
427
426
  field_type = type.arm(arm_name).fetch(field.name)
427
+ outer_c = named_type_c_name(type)
428
+
428
429
  if field_type.is_a?(Types::Nullable) && !field.value.type.is_a?(Types::Nullable) && !c_backend_pointer_like_type?(field_type.base)
429
430
  emit_nullable_some_initializer(field_type, field.value)
430
431
  elsif field.value.is_a?(IR::AddressOf) && !field_type.is_a?(Types::Nullable)
431
- c_type_name = named_type_c_name(field_type)
432
- inner = field.value.expression
433
- "((#{c_type_name}*)memcpy(malloc(sizeof(#{c_type_name})), &(#{emit_expression(inner)}), sizeof(#{c_type_name})))"
434
- elsif aggregate_field_creates_cycle?(field_type, named_type_c_name(type)) &&
435
- array_type?(field_type)
436
- elem_c_name = c_type(array_element_type(field_type))
437
- elem_count = array_length(field_type)
438
- elements = field.value.is_a?(IR::ArrayLiteral) ? field.value.elements.map { |e| emit_initializer(e) }.join(", ") : ""
439
- "((#{elem_c_name}*)memcpy(malloc(#{elem_count} * sizeof(#{elem_c_name})), &(#{elem_c_name}[#{elem_count}]){ #{elements} }, #{elem_count} * sizeof(#{elem_c_name})))"
440
- elsif aggregate_field_creates_cycle?(field_type, named_type_c_name(type)) &&
441
- !field_type.is_a?(Types::GenericInstance)
442
- field_c_name = named_type_c_name(field_type)
443
- init = emit_initializer(field.value)
444
- source_expr = if init.start_with?("{")
445
- "&(#{c_type(field_type)})#{init}"
446
- else
447
- "&(#{init})"
448
- end
449
- "((#{field_c_name}*)memcpy(malloc(sizeof(#{field_c_name})), #{source_expr}, sizeof(#{field_c_name})))"
432
+ emit_addressof_field_initializer(field_type, field.value)
433
+ elsif aggregate_field_creates_cycle?(field_type, outer_c) && array_type?(field_type)
434
+ emit_cyclic_array_initializer(field_type, field.value)
435
+ elsif aggregate_field_creates_cycle?(field_type, outer_c) && !field_type.is_a?(Types::GenericInstance)
436
+ emit_cyclic_struct_initializer(field_type, field.value)
450
437
  elsif void_storage_field?(field_type)
451
438
  emit_void_field_initializer(field.value)
452
439
  else
@@ -454,6 +441,30 @@ module MilkTea
454
441
  end
455
442
  end
456
443
 
444
+ def emit_addressof_field_initializer(field_type, value)
445
+ c_type_name = named_type_c_name(field_type)
446
+ inner = value.expression
447
+ "((#{c_type_name}*)memcpy(malloc(sizeof(#{c_type_name})), &(#{emit_expression(inner)}), sizeof(#{c_type_name})))"
448
+ end
449
+
450
+ def emit_cyclic_array_initializer(field_type, value)
451
+ elem_c_name = c_type(array_element_type(field_type))
452
+ elem_count = array_length(field_type)
453
+ elements = value.is_a?(IR::ArrayLiteral) ? value.elements.map { |e| emit_initializer(e) }.join(", ") : ""
454
+ "((#{elem_c_name}*)memcpy(malloc(#{elem_count} * sizeof(#{elem_c_name})), &(#{elem_c_name}[#{elem_count}]){ #{elements} }, #{elem_count} * sizeof(#{elem_c_name})))"
455
+ end
456
+
457
+ def emit_cyclic_struct_initializer(field_type, value)
458
+ field_c_name = named_type_c_name(field_type)
459
+ init = emit_initializer(value)
460
+ source_expr = if init.start_with?("{")
461
+ "&(#{c_type(field_type)})#{init}"
462
+ else
463
+ "&(#{init})"
464
+ end
465
+ "((#{field_c_name}*)memcpy(malloc(sizeof(#{field_c_name})), #{source_expr}, sizeof(#{field_c_name})))"
466
+ end
467
+
457
468
  def emit_void_field_initializer(expression)
458
469
  "(#{emit_expression(expression)}, 0)"
459
470
  end
@@ -342,66 +342,46 @@ module MilkTea
342
342
  function.body.any? { |statement| statement_uses_str_equality?(statement) }
343
343
  end
344
344
 
345
- def statement_uses_named_call?(statement, callees)
345
+ def any_ir_statement?(statement, expression_pred:, **kwargs)
346
346
  case statement
347
347
  when IR::LocalDecl
348
- expression_uses_named_call?(statement.value, callees)
348
+ statement.value && expression_pred.call(statement.value, **kwargs)
349
349
  when IR::Assignment
350
- expression_uses_named_call?(statement.target, callees) || expression_uses_named_call?(statement.value, callees)
350
+ expression_pred.call(statement.target, **kwargs) || expression_pred.call(statement.value, **kwargs)
351
351
  when IR::BlockStmt
352
- statement.body.any? { |inner| statement_uses_named_call?(inner, callees) }
352
+ statement.body.any? { |inner| any_ir_statement?(inner, expression_pred:, **kwargs) }
353
353
  when IR::WhileStmt
354
- expression_uses_named_call?(statement.condition, callees) || statement.body.any? { |inner| statement_uses_named_call?(inner, callees) }
354
+ expression_pred.call(statement.condition, **kwargs) ||
355
+ statement.body.any? { |inner| any_ir_statement?(inner, expression_pred:, **kwargs) }
355
356
  when IR::ForStmt
356
- statement_uses_named_call?(statement.init, callees) ||
357
- expression_uses_named_call?(statement.condition, callees) ||
358
- statement.body.any? { |inner| statement_uses_named_call?(inner, callees) } ||
359
- statement_uses_named_call?(statement.post, callees)
357
+ any_ir_statement?(statement.init, expression_pred:, **kwargs) ||
358
+ expression_pred.call(statement.condition, **kwargs) ||
359
+ statement.body.any? { |inner| any_ir_statement?(inner, expression_pred:, **kwargs) } ||
360
+ any_ir_statement?(statement.post, expression_pred:, **kwargs)
360
361
  when IR::IfStmt
361
- expression_uses_named_call?(statement.condition, callees) ||
362
- statement.then_body.any? { |inner| statement_uses_named_call?(inner, callees) } ||
363
- (statement.else_body && statement.else_body.any? { |inner| statement_uses_named_call?(inner, callees) })
362
+ expression_pred.call(statement.condition, **kwargs) ||
363
+ statement.then_body.any? { |inner| any_ir_statement?(inner, expression_pred:, **kwargs) } ||
364
+ (statement.else_body && statement.else_body.any? { |inner| any_ir_statement?(inner, expression_pred:, **kwargs) })
364
365
  when IR::SwitchStmt
365
- expression_uses_named_call?(statement.expression, callees) || statement.cases.any? { |switch_case| switch_case.body.any? { |inner| statement_uses_named_call?(inner, callees) } }
366
+ expression_pred.call(statement.expression, **kwargs) ||
367
+ statement.cases.any? { |switch_case| switch_case.body.any? { |inner| any_ir_statement?(inner, expression_pred:, **kwargs) } }
366
368
  when IR::StaticAssert
367
- expression_uses_named_call?(statement.condition, callees) || expression_uses_named_call?(statement.message, callees)
369
+ expression_pred.call(statement.condition, **kwargs) || expression_pred.call(statement.message, **kwargs)
368
370
  when IR::ReturnStmt
369
- statement.value && expression_uses_named_call?(statement.value, callees)
371
+ statement.value && expression_pred.call(statement.value, **kwargs)
370
372
  when IR::ExpressionStmt
371
- expression_uses_named_call?(statement.expression, callees)
373
+ expression_pred.call(statement.expression, **kwargs)
374
+ else
375
+ false
372
376
  end
373
377
  end
374
378
 
379
+ def statement_uses_named_call?(statement, callees)
380
+ any_ir_statement?(statement, expression_pred: ->(e) { expression_uses_named_call?(e, callees) })
381
+ end
382
+
375
383
  def statement_uses_str_equality?(statement)
376
- case statement
377
- when IR::LocalDecl
378
- expression_uses_str_equality?(statement.value)
379
- when IR::Assignment
380
- expression_uses_str_equality?(statement.target) || expression_uses_str_equality?(statement.value)
381
- when IR::BlockStmt
382
- statement.body.any? { |inner| statement_uses_str_equality?(inner) }
383
- when IR::WhileStmt
384
- expression_uses_str_equality?(statement.condition) || statement.body.any? { |inner| statement_uses_str_equality?(inner) }
385
- when IR::ForStmt
386
- statement_uses_str_equality?(statement.init) ||
387
- expression_uses_str_equality?(statement.condition) ||
388
- statement.body.any? { |inner| statement_uses_str_equality?(inner) } ||
389
- statement_uses_str_equality?(statement.post)
390
- when IR::IfStmt
391
- expression_uses_str_equality?(statement.condition) ||
392
- statement.then_body.any? { |inner| statement_uses_str_equality?(inner) } ||
393
- (statement.else_body && statement.else_body.any? { |inner| statement_uses_str_equality?(inner) })
394
- when IR::SwitchStmt
395
- expression_uses_str_equality?(statement.expression) || statement.cases.any? { |switch_case| switch_case.body.any? { |inner| statement_uses_str_equality?(inner) } }
396
- when IR::StaticAssert
397
- expression_uses_str_equality?(statement.condition) || expression_uses_str_equality?(statement.message)
398
- when IR::ReturnStmt
399
- statement.value && expression_uses_str_equality?(statement.value)
400
- when IR::ExpressionStmt
401
- expression_uses_str_equality?(statement.expression)
402
- else
403
- false
404
- end
384
+ any_ir_statement?(statement, expression_pred: ->(e) { expression_uses_str_equality?(e) })
405
385
  end
406
386
 
407
387
  def expression_uses_named_call?(expression, callees)
@@ -241,21 +241,15 @@ module MilkTea
241
241
  end
242
242
 
243
243
  def collect_soa_from_statements(statements, soa_types, visited)
244
- statements.each do |stmt|
245
- case stmt
246
- when IR::LocalDecl
247
- collect_soa_type(stmt.type, soa_types, visited)
248
- when IR::BlockStmt
249
- collect_soa_from_statements(stmt.body, soa_types, visited)
250
- when IR::IfStmt
251
- collect_soa_from_statements(stmt.then_body || [], soa_types, visited)
252
- collect_soa_from_statements(stmt.else_body || [], soa_types, visited)
253
- when IR::WhileStmt
254
- collect_soa_from_statements(stmt.body || [], soa_types, visited)
255
- when IR::ForStmt
256
- collect_soa_from_statements(stmt.body || [], soa_types, visited)
257
- end
258
- end
244
+ each_ir_statement_for_type_collection(statements,
245
+ type_collector_method: :collect_soa_type,
246
+ expression_walker_method: :collect_soa_type_in_expression,
247
+ accumulator: soa_types, visited:)
248
+ end
249
+
250
+ def collect_soa_type_in_expression(expression, soa_types, visited)
251
+ return unless expression.respond_to?(:type)
252
+ collect_soa_type(expression.type, soa_types, visited)
259
253
  end
260
254
 
261
255
  def collect_soa_type(type, soa_types, visited)
@@ -290,21 +284,15 @@ module MilkTea
290
284
  end
291
285
 
292
286
  def collect_simd_from_statements(statements, simd_types, visited)
293
- statements.each do |stmt|
294
- case stmt
295
- when IR::LocalDecl
296
- collect_simd_type(stmt.type, simd_types, visited)
297
- when IR::BlockStmt
298
- collect_simd_from_statements(stmt.body, simd_types, visited)
299
- when IR::IfStmt
300
- collect_simd_from_statements(stmt.then_body || [], simd_types, visited)
301
- collect_simd_from_statements(stmt.else_body || [], simd_types, visited)
302
- when IR::WhileStmt
303
- collect_simd_from_statements(stmt.body || [], simd_types, visited)
304
- when IR::ForStmt
305
- collect_simd_from_statements(stmt.body || [], simd_types, visited)
306
- end
307
- end
287
+ each_ir_statement_for_type_collection(statements,
288
+ type_collector_method: :collect_simd_type,
289
+ expression_walker_method: :collect_simd_type_in_expression,
290
+ accumulator: simd_types, visited:)
291
+ end
292
+
293
+ def collect_simd_type_in_expression(expression, simd_types, visited)
294
+ return unless expression.respond_to?(:type)
295
+ collect_simd_type(expression.type, simd_types, visited)
308
296
  end
309
297
 
310
298
  def collect_simd_type(type, simd_types, visited)
@@ -61,12 +61,6 @@ module MilkTea
61
61
  lines
62
62
  end
63
63
 
64
- def variant_self_reference?(type, outer_c)
65
- return false unless type.is_a?(Types::Variant) || type.is_a?(Types::VariantInstance)
66
-
67
- named_type_c_name(type) == outer_c
68
- end
69
-
70
64
  def emit_variant(variant_decl)
71
65
  lines = []
72
66
  outer_c = variant_decl.linkage_name
@@ -38,6 +38,20 @@ module MilkTea
38
38
  # scanning a 600 KB source file) never trigger a false positive, yet low
39
39
  # enough that a genuinely infinite loop fires a clear fatal within ~1 s.
40
40
  LOOP_GUARD_MAX_ITERATIONS = 50_000_000
41
+ EQUALITY_OPERATORS = ["==", "!="].freeze
42
+
43
+ include AggregateUtils
44
+ include TypeSystem
45
+ include Reachability
46
+ include FeatureDetection
47
+ include TypeCollectors
48
+ include Statements
49
+ include ControlFlowEmission
50
+ include RuntimeHelpers
51
+ include FormatHelpers
52
+ include TypeDeclaration
53
+ include Expressions
54
+ include Reinterpret
41
55
 
42
56
  def self.emit(program, emit_line_directives: true, debug_guards: false)
43
57
  new(program, emit_line_directives:, debug_guards:).emit
@@ -61,11 +75,27 @@ module MilkTea
61
75
 
62
76
  def emit
63
77
  lines = []
78
+ @cyclic_aggregate_pairs = build_cyclic_aggregate_pairs(
79
+ emitted_aggregate_structs + collect_generic_struct_decls + collect_task_decls + collect_proc_decls + collect_dyn_decls + collect_str_buffer_decls + collect_nullable_opt_decls +
80
+ emitted_aggregate_unions +
81
+ emitted_aggregate_variants + collect_generic_variant_decls
82
+ )
83
+
64
84
  constants = emitted_constants
65
- all_struct_decls = emitted_aggregate_structs + collect_generic_struct_decls + collect_task_decls + collect_proc_decls + collect_dyn_decls + collect_str_buffer_decls + collect_nullable_opt_decls
66
- all_variant_decls = emitted_aggregate_variants + collect_generic_variant_decls
67
- all_decls_for_cycle = all_struct_decls + emitted_aggregate_unions + all_variant_decls
68
- @cyclic_aggregate_pairs = build_cyclic_aggregate_pairs(all_decls_for_cycle)
85
+
86
+ emit_preamble(lines)
87
+ emit_conditional_helpers(lines)
88
+ emit_type_declarations(lines)
89
+ emit_function_forward_declarations(lines)
90
+ emit_globals_and_constants(lines, constants)
91
+ emit_per_type_helpers(lines)
92
+ emit_string_literal_constants_section(lines)
93
+ emit_function_definitions(lines)
94
+
95
+ lines.join("\n").rstrip + "\n"
96
+ end
97
+
98
+ def emit_preamble(lines)
69
99
  headers = @program.includes.map(&:header)
70
100
  if headers.include?("\"fs_support.h\"") || headers.include?("\"tls_support.h\"") || uses_parallel_for_helper? || uses_spawn_all_helper? || uses_detach_helper?
71
101
  lines << "#ifndef _GNU_SOURCE"
@@ -89,70 +119,61 @@ module MilkTea
89
119
  lines << "#include #{header}"
90
120
  end
91
121
  lines << ""
122
+ end
92
123
 
124
+ def emit_conditional_helpers(lines)
93
125
  if uses_string_view?
94
126
  lines.concat(emit_string_type)
95
127
  lines << ""
96
128
  end
97
-
98
129
  if uses_vector_math_types?
99
130
  lines.concat(emit_vector_math_types)
100
131
  lines << ""
101
132
  end
102
-
103
133
  if uses_fatal_helper?
104
134
  lines.concat(emit_fatal_helper)
105
135
  lines << ""
106
136
  end
107
-
108
137
  if uses_format_helpers?
109
138
  lines.concat(emit_format_helpers)
110
139
  lines << ""
111
140
  end
112
-
113
141
  if uses_fmt_builder?
114
142
  lines.concat(emit_fmt_builder_helpers)
115
143
  lines << ""
116
144
  end
117
-
118
145
  if uses_str_equality_helper?
119
146
  lines.concat(emit_str_equality_helper)
120
147
  lines << ""
121
148
  end
122
-
123
149
  if uses_text_buffer_helpers?
124
150
  lines.concat(emit_text_buffer_helpers)
125
151
  lines << ""
126
152
  end
127
-
128
153
  if uses_async_memory_helpers?
129
154
  lines.concat(emit_async_memory_helpers)
130
155
  lines << ""
131
156
  end
132
-
133
157
  if uses_parallel_for_helper?
134
158
  lines.concat(emit_parallel_for_helper)
135
159
  lines << ""
136
160
  end
137
-
138
161
  if uses_spawn_all_helper?
139
162
  lines.concat(emit_spawn_all_helper)
140
163
  lines << ""
141
164
  end
142
-
143
165
  if uses_detach_helper?
144
166
  lines.concat(emit_detach_helpers)
145
167
  lines << ""
146
168
  end
169
+ end
147
170
 
148
- opaque_decls = @program.opaques
149
- aggregate_decls = sort_aggregate_decls(
150
- all_struct_decls,
151
- emitted_aggregate_unions,
152
- all_variant_decls,
153
- )
171
+ def emit_type_declarations(lines)
172
+ all_struct_decls = emitted_aggregate_structs + collect_generic_struct_decls + collect_task_decls + collect_proc_decls + collect_dyn_decls + collect_str_buffer_decls + collect_nullable_opt_decls
173
+ all_variant_decls = emitted_aggregate_variants + collect_generic_variant_decls
174
+ aggregate_decls = sort_aggregate_decls(all_struct_decls, emitted_aggregate_unions, all_variant_decls)
154
175
 
155
- forward_declarations = emit_forward_declarations(opaque_decls, aggregate_decls)
176
+ forward_declarations = emit_forward_declarations(@program.opaques, aggregate_decls)
156
177
  unless forward_declarations.empty?
157
178
  lines.concat(forward_declarations)
158
179
  lines << ""
@@ -222,13 +243,17 @@ module MilkTea
222
243
  lines.concat(emit_variant_equality_helpers)
223
244
  lines << ""
224
245
  end
246
+ end
225
247
 
248
+ def emit_function_forward_declarations(lines)
226
249
  function_declarations = emit_function_declarations(emitted_functions)
227
250
  unless function_declarations.empty?
228
251
  lines.concat(function_declarations)
229
252
  lines << ""
230
253
  end
254
+ end
231
255
 
256
+ def emit_globals_and_constants(lines, constants)
232
257
  constants.each do |constant|
233
258
  @current_line = constant.line if constant.line
234
259
  @source_path ||= constant.path if constant.path
@@ -248,66 +273,50 @@ module MilkTea
248
273
  lines << emit_static_assert(statement)
249
274
  end
250
275
  lines << "" unless @program.static_asserts.empty?
276
+ end
251
277
 
252
- reinterpret_helpers = collect_reinterpret_helpers
253
- reinterpret_helpers.each do |helper|
278
+ def emit_per_type_helpers(lines)
279
+ collect_reinterpret_helpers.each do |helper|
254
280
  lines.concat(emit_reinterpret_helper(helper))
255
281
  lines << ""
256
282
  end
257
283
 
258
- checked_array_index_types = collect_checked_array_index_types
259
- checked_array_index_types.each do |type|
284
+ collect_checked_array_index_types.each do |type|
260
285
  lines.concat(emit_checked_array_index_helper(type))
261
286
  lines << ""
262
287
  end
263
288
 
264
- checked_span_index_types = collect_checked_span_index_types
265
- checked_span_index_types.each do |type|
289
+ collect_checked_span_index_types.each do |type|
266
290
  lines.concat(emit_checked_span_index_helper(type))
267
291
  lines << ""
268
292
  end
269
293
 
270
- nullable_array_index_types = collect_checked_array_index_types(nullable_only: true)
271
- nullable_array_index_types.each do |type|
294
+ collect_checked_array_index_types(nullable_only: true).each do |type|
272
295
  lines.concat(emit_nullable_array_index_helper(type))
273
296
  lines << ""
274
297
  end
275
298
 
276
- nullable_span_index_types = collect_checked_span_index_types(nullable_only: true)
277
- nullable_span_index_types.each do |type|
299
+ collect_checked_span_index_types(nullable_only: true).each do |type|
278
300
  lines.concat(emit_nullable_span_index_helper(type))
279
301
  lines << ""
280
302
  end
303
+ end
281
304
 
305
+ def emit_string_literal_constants_section(lines)
282
306
  str_literals = collect_str_literals
283
- unless str_literals.empty?
284
- @str_literal_map = {}
285
- str_literals.each_with_index { |value, i| @str_literal_map[value] = str_literal_name(i) }
286
- lines.concat(emit_str_literal_constants(str_literals))
287
- lines << ""
288
- end
307
+ return if str_literals.empty?
289
308
 
309
+ @str_literal_map = {}
310
+ str_literals.each_with_index { |value, i| @str_literal_map[value] = str_literal_name(i) }
311
+ lines.concat(emit_str_literal_constants(str_literals))
312
+ lines << ""
313
+ end
314
+
315
+ def emit_function_definitions(lines)
290
316
  emitted_functions.each do |function|
291
317
  lines.concat(emit_function(function))
292
318
  lines << ""
293
319
  end
294
-
295
- lines.join("\n").rstrip + "\n"
296
320
  end
297
-
298
- EQUALITY_OPERATORS = ["==", "!="].freeze
299
-
300
- include AggregateUtils
301
- include TypeSystem
302
- include Reachability
303
- include FeatureDetection
304
- include TypeCollectors
305
- include Statements
306
- include ControlFlowEmission
307
- include RuntimeHelpers
308
- include FormatHelpers
309
- include TypeDeclaration
310
- include Expressions
311
- include Reinterpret
312
321
  end
313
322
  end