mt-lang 0.3.30 → 0.3.32
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/README.md +9 -7
- data/docs/index.html +24 -5
- data/docs/language-design.md +3 -1
- data/docs/language-manual.md +61 -5
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/bindings/bindgen/emitter.rb +1 -8
- data/lib/milk_tea/core/c_backend/expressions.rb +22 -2
- data/lib/milk_tea/core/c_backend/feature_detection.rb +114 -24
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +89 -20
- data/lib/milk_tea/core/c_backend.rb +33 -5
- data/lib/milk_tea/core/compile_time.rb +25 -0
- data/lib/milk_tea/core/lexer.rb +5 -1
- data/lib/milk_tea/core/lowering/declarations.rb +8 -0
- data/lib/milk_tea/core/parser/declarations.rb +9 -4
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +35 -6
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +45 -3
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +31 -1
- data/std/c/box2d.mt +1 -1
- data/std/c/cgltf.mt +8 -8
- data/std/c/cjson.mt +1 -1
- data/std/c/curl.mt +5 -5
- data/std/c/enet.mt +3 -3
- data/std/c/flecs.mt +31 -31
- data/std/c/libuv.mt +50 -50
- data/std/c/miniaudio.mt +20 -20
- data/std/c/raygui.mt +1 -1
- data/std/c/raylib.mt +1 -1
- data/std/c/rpng.mt +1 -1
- data/std/c/rres.mt +2 -2
- data/std/c/sdl3.mt +56 -56
- data/std/c/sqlite3.mt +1 -1
- data/std/c/stb_truetype.mt +1 -1
- data/std/sdl3/runtime.mt +11 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0210a7132e7230d11f390c51bf60c7ff5a19b0d7716737b3ed5ac89b4a773842
|
|
4
|
+
data.tar.gz: 54d1170e9f9630b6747062b2ee8d285cb3b8cb83f5399469ba3b7908f81ad654
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ca666aaa8bceb581fbe3a54c315127a3dbe3bb1a7a122027d9af44f96c3095d352d3f10f65f3e4d31b4992bca017eca600e7216c16a9a71ec81f1d0fb8091a7
|
|
7
|
+
data.tar.gz: 6d143563d49aae6636133feaa6bac2b0bee00e468d972c48db3d665692624341f4d5cbbe880fe9ba91aa0aec7da0c253df1facc5d4c405ddcd44778e67ca7ca9
|
data/README.md
CHANGED
|
@@ -94,7 +94,7 @@ Supported literals:
|
|
|
94
94
|
- floats: `3.14`, `1.2e-3`, `1.0f` (float suffix), `1.0d` (double suffix)
|
|
95
95
|
- character: `'a'`, `'\n'`, `'\t'`, `'\\'`, `'\''`, `'\0'`, `'\x41'`. Type is `ubyte`. Escape sequences: `\n`, `\r`, `\t`, `\\`, `\'`, `\"`, `\0` (null byte), `\xNN` (hex byte).
|
|
96
96
|
- booleans: `true`, `false`
|
|
97
|
-
- string: `"hello"` -> `str`. The `+` operator concatenates `str` values
|
|
97
|
+
- string: `"hello"` -> `str`. The `+` operator concatenates `str` values (see §12).
|
|
98
98
|
- cstring: `c"hello"` -> `cstr`
|
|
99
99
|
- heredoc string: `<<-TAG ... TAG`
|
|
100
100
|
- heredoc cstring: `c<<-TAG ... TAG`
|
|
@@ -106,7 +106,7 @@ Common punctuation and operators:
|
|
|
106
106
|
- delimiters: `(` `)` `[` `]`
|
|
107
107
|
- access and separators: `:` `,` `.`
|
|
108
108
|
- type markers: `->` `?`
|
|
109
|
-
- arithmetic: `+ - * / %`
|
|
109
|
+
- arithmetic: `+ - * / %`
|
|
110
110
|
- bitwise: `~ & | ^ << >>`
|
|
111
111
|
- comparison: `== != < <= > >=`
|
|
112
112
|
- assignment: `= += -= *= /= %= &= |= ^= <<= >>=`
|
|
@@ -332,6 +332,8 @@ Rules:
|
|
|
332
332
|
|
|
333
333
|
Enum and flags values support the full set of comparison operators (`==`, `!=`, `<`, `<=`, `>`, `>=`) against values of the same enum type and against their backing integer type. Comparisons use the underlying integer backing values. Flags also support bitwise operators (`|`, `&`, `^`, `~`).
|
|
334
334
|
|
|
335
|
+
Struct and variant values support `==` and `!=`: structs compare field by field when all fields are comparable, variants compare the active arm and its payload. Recursive variants compare cyclic fields by pointer identity. Untagged `union` fields and non-comparable types (`proc`, `span`, `simd`, `SoA`, `Task`, `dyn`) are rejected — use `equal[T]` there.
|
|
336
|
+
|
|
335
337
|
Generic variants and structs are supported, for example `Option[int]`.
|
|
336
338
|
|
|
337
339
|
## 6. Interfaces And Methods
|
|
@@ -808,7 +810,7 @@ Type constructors:
|
|
|
808
810
|
|
|
809
811
|
- `ptr[T]`
|
|
810
812
|
- `const_ptr[T]`
|
|
811
|
-
- `own[T]` — owning heap pointer
|
|
813
|
+
- `own[T]` — owning heap pointer with auto-deref; storable, returnable, nullable (compiles to `T*`)
|
|
812
814
|
- `ref[T]`
|
|
813
815
|
- `span[T]`
|
|
814
816
|
- `array[T, N]`
|
|
@@ -819,9 +821,9 @@ Type constructors:
|
|
|
819
821
|
- `fn(params...) -> R`
|
|
820
822
|
- `proc(params...) -> R`
|
|
821
823
|
- `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`
|
|
822
|
-
- `simd[T, N]` — SIMD vector
|
|
824
|
+
- `simd[T, N]` — SIMD vector of `N` numeric lanes (128 or 256 bits); component-wise `+` `-` `*` `/` `%` `&` `|` `^` `~` `<<` `>>`; lane access via `[i]`. Lowers to GCC/Clang vector extensions.
|
|
823
825
|
- `dyn[InterfaceName]` — runtime interface value (fat pointer: `{ void* data, void* vtable }`). Constructed via `adapt[Interface](value: ref[T])`. @see §6.
|
|
824
|
-
- `atomic[T]` —
|
|
826
|
+
- `atomic[T]` — lock-free atomic value (`T` = primitive integer or `bool`) with `load`/`store`/`add`/`sub`/`exchange`; sequential consistency. Lowers to C11 `_Atomic T`.
|
|
825
827
|
- `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
|
|
826
828
|
|
|
827
829
|
When a `span[T]` is expected, an addressable `array[T, N]` value may be passed directly via implicit boundary coercion. For explicit conversion, `array.as_span()` returns `span[T]` without requiring a boundary context.
|
|
@@ -1141,8 +1143,8 @@ Current compiler rejects:
|
|
|
1141
1143
|
|
|
1142
1144
|
### Operator and expression restrictions
|
|
1143
1145
|
|
|
1144
|
-
- `+`
|
|
1145
|
-
-
|
|
1146
|
+
- `+` concatenates `str`; `cstr` and mixed `str`/`cstr` concatenation are not supported
|
|
1147
|
+
- `==`/`!=` on structs and variants requires all fields equality-comparable; use `equal[T]` otherwise
|
|
1146
1148
|
- range expressions are restricted to `for`-loop iterables and range-index assignment targets
|
|
1147
1149
|
- functions, methods, generic functions, and variant arms must be called — they are not usable as bare values
|
|
1148
1150
|
- `read(...)` of a raw pointer requires `unsafe`
|
data/docs/index.html
CHANGED
|
@@ -961,7 +961,7 @@ let total = (
|
|
|
961
961
|
<tr><td>Delimiters</td><td><code>( ) [ ]</code></td></tr>
|
|
962
962
|
<tr><td>Separators / Access</td><td><code>: , .</code></td></tr>
|
|
963
963
|
<tr><td>Type markers</td><td><code>-></code> <code>?</code></td></tr>
|
|
964
|
-
<tr><td>Arithmetic</td><td><code>+ - * / %</code
|
|
964
|
+
<tr><td>Arithmetic</td><td><code>+ - * / %</code></td></tr>
|
|
965
965
|
<tr><td>Bitwise</td><td><code>~ & | ^ << >></code></td></tr>
|
|
966
966
|
<tr><td>Comparison</td><td><code>== != < <= > >=</code></td></tr>
|
|
967
967
|
<tr><td>Assignment</td><td><code>= += -= *= /= %= &= |= ^= <<= >>=</code></td></tr>
|
|
@@ -1149,6 +1149,25 @@ variant Result[T, E]:
|
|
|
1149
1149
|
<strong>Prelude types.</strong> <code>Option[T]</code> and <code>Result[T, E]</code> are auto-imported — no <code>import</code> statement is needed. They are available in every Milk Tea source file. Their extending methods (<code>is_some</code>, <code>unwrap</code>, <code>is_success</code>, <code>map_error</code>, etc.) are always accessible.
|
|
1150
1150
|
</div>
|
|
1151
1151
|
|
|
1152
|
+
<h3>Struct & Variant Equality</h3>
|
|
1153
|
+
<p>Structs and variants support <code>==</code> and <code>!=</code> with field-wise comparison. A struct compares equal when every field compares equal; a variant compares equal when the same arm is active and its payload fields are equal. Every field must be equality-comparable (nested structs, arrays, nullable values, <code>str</code>, enums, flags, pointers, ...). Recursive variants embed cyclic fields as pointers and compare those by pointer identity. Untagged <code>union</code> fields and non-comparable types (<code>proc</code>, <code>span</code>, <code>simd</code>, <code>SoA</code>, <code>Task</code>, <code>dyn</code>) are rejected; use <code>equal[T](...)</code> there.</p>
|
|
1154
|
+
<div class="code-wrap">
|
|
1155
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1156
|
+
<pre><code>struct Vec2:
|
|
1157
|
+
x: float
|
|
1158
|
+
y: float
|
|
1159
|
+
|
|
1160
|
+
variant Shape:
|
|
1161
|
+
circle(radius: float)
|
|
1162
|
+
square(side: float)
|
|
1163
|
+
|
|
1164
|
+
function same_point(a: Vec2, b: Vec2) -> bool:
|
|
1165
|
+
return a == b
|
|
1166
|
+
|
|
1167
|
+
function same_shape(a: Shape, b: Shape) -> bool:
|
|
1168
|
+
return a == b</code></pre>
|
|
1169
|
+
</div>
|
|
1170
|
+
|
|
1152
1171
|
<h3>Opaque</h3>
|
|
1153
1172
|
<p>For C handles whose layout is unknown. Opaque types may implement interfaces, enabling constrained generics and <code>dyn</code> dispatch over C handles.</p>
|
|
1154
1173
|
<div class="code-wrap">
|
|
@@ -1966,8 +1985,8 @@ MSG</code></pre>
|
|
|
1966
1985
|
<tr><td><code>%</code> requires integer operands</td><td></td></tr>
|
|
1967
1986
|
<tr><td>Bitwise ops require matching types</td><td>Integer or flags types</td></tr>
|
|
1968
1987
|
<tr><td>Shift ops require integer operands</td><td></td></tr>
|
|
1969
|
-
<tr><td>
|
|
1970
|
-
<tr><td>No <code>
|
|
1988
|
+
<tr><td>Aggregate equality requires comparable fields</td><td>Structs and variants support <code>==</code>/<code>!=</code> when every field is equality-comparable. Untagged <code>union</code> fields and non-comparable types (<code>proc</code>, <code>span</code>, <code>simd</code>, <code>SoA</code>, <code>Task</code>, <code>dyn</code>) are rejected — use <code>equal[T](a, b)</code> there. Recursive variants compare cyclic fields by pointer identity.</td></tr>
|
|
1989
|
+
<tr><td>No <code>cstr</code> concatenation</td><td><code>+</code> concatenates <code>str</code>; <code>cstr</code> and mixed <code>str</code>/<code>cstr</code> are rejected</td></tr>
|
|
1971
1990
|
<tr><td>Compile-time constant fit</td><td>Exact compile-time numeric constants (literals, <code>const</code> values) fit an explicit numeric target without a manual cast when representable exactly</td></tr>
|
|
1972
1991
|
<tr><td>Integer-to-float at typed boundaries</td><td>A primitive integer expression may flow into an expected float type for explicit typed locals, assignments, returns, function arguments, or field initializers. Integer arithmetic stays integer arithmetic until that final boundary cast.</td></tr>
|
|
1973
1992
|
</table>
|
|
@@ -2408,8 +2427,8 @@ import std.math # intentionally available for downstream</code></pre>
|
|
|
2408
2427
|
<li>Conditions must be <code>bool</code>; no truthy/falsy coercion</li>
|
|
2409
2428
|
<li>Mixed signed/unsigned arithmetic requires explicit cast; non-widening integer conversions (narrowing, signed → unsigned) need explicit <code>T<-value</code></li>
|
|
2410
2429
|
<li>Enum and flags values do not implicitly coerce to backing integers</li>
|
|
2411
|
-
<li><code>+</code>
|
|
2412
|
-
<li><code>==</code
|
|
2430
|
+
<li><code>+</code> concatenates <code>str</code>; <code>cstr</code> and mixed <code>str</code>/<code>cstr</code> are not concatenable</li>
|
|
2431
|
+
<li><code>==</code>/<code>!=</code> on structs and variants requires all fields equality-comparable; use <code>equal[T]</code> for non-comparable types</li>
|
|
2413
2432
|
<li>A statement cannot begin with a binary operator (including <code>+</code>, <code>-</code>, <code>and</code>, <code>or</code>, <code>is</code>, <code>..</code>); continuation requires ending the previous line with the operator or wrapping in <code>()</code></li>
|
|
2414
2433
|
<li>Bare function and method names are not usable as values (cannot be assigned or passed without calling). Use <code>fn(...)</code> function pointer types or <code>proc(...)</code> closures for callable values.</li>
|
|
2415
2434
|
</ul>
|
data/docs/language-design.md
CHANGED
|
@@ -755,13 +755,15 @@ Explicit specialization arguments may be type references like `bytes_for[int](4)
|
|
|
755
755
|
|
|
756
756
|
Built-in operators should match familiar C behavior where possible:
|
|
757
757
|
|
|
758
|
-
- arithmetic: `+ - * / %`
|
|
758
|
+
- arithmetic: `+ - * / %`
|
|
759
759
|
- comparison: `== != < <= > >=`
|
|
760
760
|
- boolean: `and or not`
|
|
761
761
|
- bitwise: `& | ^ ~ << >>`
|
|
762
762
|
|
|
763
763
|
No user-defined operator overloading.
|
|
764
764
|
|
|
765
|
+
The `+` operator also concatenates two `str` values, allocating a new heap-backed string; `cstr` values are not concatenable. This is the one operator exception to the arithmetic rules, and it is described in the text-construction rule under Design rules §1.
|
|
766
|
+
|
|
765
767
|
Built-in vector, matrix, and quaternion types support component-wise arithmetic with the standard operators:
|
|
766
768
|
|
|
767
769
|
- Vectors (`vecN`/`ivecN`): `+`, `-`, `*` (component-wise same-type); `*`, `/` (scalar); unary `-`
|
data/docs/language-manual.md
CHANGED
|
@@ -124,7 +124,7 @@ Supported literals:
|
|
|
124
124
|
- integer: `42`, `0xff`, `0b1010`, with `_` separators. Integer type suffixes: `42u` (`uint`), `0xFFub` (`ubyte`), `100z` (`ptr_uint`), `7i` (`int`), `-1l` (`long`), etc.
|
|
125
125
|
- float: `3.14`, `1.2e-3`, `1.1920929E-7`, `1.0f` (float suffix), `1.0d` (double suffix)
|
|
126
126
|
- character: `'a'`, `'\n'`, `'\t'`, `'\\'`, `'\''`, `'\0'`, `'\x41'`. Type is `ubyte`. Escape sequences: `\n`, `\r`, `\t`, `\\`, `\'`, `\"`, `\0` (null), `\xNN` (hex byte).
|
|
127
|
-
- string: `"hello"` (`str`).
|
|
127
|
+
- string: `"hello"` (`str`). String escapes: `\n`, `\r`, `\t`, `\0`, `\"`, `\'`, `\\`; any other `\x` sequence is taken literally, and hex byte escapes (`\xNN`) are character-literal only. `str + str` concatenates (§5.3); see §12 for text-building details.
|
|
128
128
|
- cstring: `c"hello"` (`cstr`)
|
|
129
129
|
- heredoc string: `<<-TAG ... TAG` (`str`)
|
|
130
130
|
- heredoc cstring: `c<<-TAG ... TAG` (`cstr`)
|
|
@@ -412,6 +412,60 @@ const IS_AFTER: bool = State.running > State.idle
|
|
|
412
412
|
|
|
413
413
|
Flags values also support bitwise operators (`|`, `&`, `^`, `~`) where both operands share the same flags type.
|
|
414
414
|
|
|
415
|
+
### 3.4b Struct and variant equality
|
|
416
|
+
|
|
417
|
+
Struct and variant values support `==` and `!=`. The compiler generates a
|
|
418
|
+
field-wise comparison helper per compared aggregate type.
|
|
419
|
+
|
|
420
|
+
Rules for structs:
|
|
421
|
+
|
|
422
|
+
- Two struct values compare equal when every field compares equal, in field
|
|
423
|
+
order. Comparison is field-by-field, never a raw byte compare.
|
|
424
|
+
- A struct is equality-comparable when every field is itself
|
|
425
|
+
equality-comparable: primitives, enums, flags, `str`, pointers, `ref[T]`,
|
|
426
|
+
`fn(...)`, opaque, other comparable structs, comparable variants, arrays of
|
|
427
|
+
comparable elements, and value nullables of comparable bases.
|
|
428
|
+
- Untagged `union` fields and non-comparable field types (`proc`, `span`,
|
|
429
|
+
`simd`, `SoA`, `Task`, `dyn`) are rejected with a field-level error. Use
|
|
430
|
+
`equal[T](...)` for those.
|
|
431
|
+
|
|
432
|
+
Rules for variants:
|
|
433
|
+
|
|
434
|
+
- Two variant values compare equal when the same arm is active and every
|
|
435
|
+
payload field of that arm compares equal. No-payload arms compare by
|
|
436
|
+
discriminant only.
|
|
437
|
+
- Recursive (cyclic) variants are supported: the C backend embeds cyclic
|
|
438
|
+
fields as pointers, and those fields compare by pointer identity. A
|
|
439
|
+
separately-constructed recursive value therefore compares unequal to
|
|
440
|
+
another value of identical shape.
|
|
441
|
+
- Variant arm payload bindings (`match v: Token.ident as p:`) compare as
|
|
442
|
+
payload structs.
|
|
443
|
+
|
|
444
|
+
General rules:
|
|
445
|
+
|
|
446
|
+
- `==`/`!=` on struct and variant constants folds at compile time.
|
|
447
|
+
- Comparison operands must have the same aggregate type.
|
|
448
|
+
- The canonical `equal[T](...)` hook remains available for types without
|
|
449
|
+
`==` support and for custom equality semantics.
|
|
450
|
+
|
|
451
|
+
Example:
|
|
452
|
+
|
|
453
|
+
```mt
|
|
454
|
+
struct Vec2:
|
|
455
|
+
x: float
|
|
456
|
+
y: float
|
|
457
|
+
|
|
458
|
+
variant Shape:
|
|
459
|
+
circle(radius: float)
|
|
460
|
+
square(side: float)
|
|
461
|
+
|
|
462
|
+
function same_point(a: Vec2, b: Vec2) -> bool:
|
|
463
|
+
return a == b
|
|
464
|
+
|
|
465
|
+
function same_shape(a: Shape, b: Shape) -> bool:
|
|
466
|
+
return a == b
|
|
467
|
+
```
|
|
468
|
+
|
|
415
469
|
### 3.5 Interfaces
|
|
416
470
|
|
|
417
471
|
```mt
|
|
@@ -1015,9 +1069,11 @@ Rules:
|
|
|
1015
1069
|
8. `==`, `!=`
|
|
1016
1070
|
9. `<`, `<=`, `>`, `>=`
|
|
1017
1071
|
10. `<<`, `>>`
|
|
1018
|
-
11. `+`, `-`
|
|
1072
|
+
11. `+`, `-`
|
|
1019
1073
|
12. `*`, `/`, `%`
|
|
1020
1074
|
|
|
1075
|
+
The `+` operator also concatenates two `str` operands, producing a new heap-backed `str` (§2.3). It does not concatenate `cstr` or mixed `str`/`cstr` operands.
|
|
1076
|
+
|
|
1021
1077
|
### 5.4 Assignment operators
|
|
1022
1078
|
|
|
1023
1079
|
- `=`
|
|
@@ -1322,7 +1378,7 @@ Custom formatting hook notes:
|
|
|
1322
1378
|
- `read(...)` of raw pointer requires `unsafe`
|
|
1323
1379
|
- pointer casts require `unsafe`
|
|
1324
1380
|
- `reinterpret[...]` requires `unsafe`, non-array concrete sized types, and equal-size source and target types.
|
|
1325
|
-
- variant types support `==` and `!=` (generates per-
|
|
1381
|
+
- struct and variant types support `==` and `!=` (generates a per-type comparison helper; see §3.4b)
|
|
1326
1382
|
|
|
1327
1383
|
## 10. Async Semantics
|
|
1328
1384
|
|
|
@@ -1564,8 +1620,8 @@ The compiler intentionally rejects the following patterns. These are design cons
|
|
|
1564
1620
|
|
|
1565
1621
|
### 13.7 Operator and expression restrictions
|
|
1566
1622
|
|
|
1567
|
-
- `+`
|
|
1568
|
-
-
|
|
1623
|
+
- `+` concatenates `str`; `cstr` and mixed `str`/`cstr` are not concatenable
|
|
1624
|
+
- `==`/`!=` on structs and variants requires all fields equality-comparable; use `equal[T]` otherwise (§3.4b)
|
|
1569
1625
|
- range expressions are restricted to `for`-loop iterables and range-index assignment targets
|
|
1570
1626
|
- functions, methods, generic functions, and variant arms must be called — they are not usable as bare values
|
|
1571
1627
|
- `read(...)` of a raw pointer requires `unsafe`
|
data/lib/milk_tea/base.rb
CHANGED
|
@@ -76,19 +76,12 @@ module MilkTea
|
|
|
76
76
|
fields = Array(node["inner"]).select { |child| child["kind"] == "FieldDecl" }
|
|
77
77
|
fields.each do |field|
|
|
78
78
|
field_type = aggregate_field_type(field, owner_name: name, aggregate_node: node)
|
|
79
|
-
mt_name
|
|
79
|
+
mt_name = emitted_name(aggregate_field_name(field, aggregate_node: node))
|
|
80
80
|
lines << " #{mt_name}: #{field_type}"
|
|
81
81
|
end
|
|
82
82
|
lines
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
def bindgen_field_name(name)
|
|
86
|
-
name = emitted_name(name)
|
|
87
|
-
return [name, nil] unless Token::KEYWORDS.key?(name)
|
|
88
|
-
|
|
89
|
-
["#{name}_", nil]
|
|
90
|
-
end
|
|
91
|
-
|
|
92
85
|
def bindgen_param_name(name)
|
|
93
86
|
name = emitted_name(name)
|
|
94
87
|
return [name, nil] unless generated_binding_name_conflict?(name)
|
|
@@ -47,6 +47,7 @@ module MilkTea
|
|
|
47
47
|
when IR::Binary
|
|
48
48
|
return emit_string_equality_expression(expression) if string_equality_expression?(expression)
|
|
49
49
|
return emit_variant_equality_expression(expression) if variant_equality_expression?(expression)
|
|
50
|
+
return emit_struct_equality_expression(expression) if struct_equality_expression?(expression)
|
|
50
51
|
return emit_nullable_null_comparison(expression) if nullable_null_comparison?(expression)
|
|
51
52
|
|
|
52
53
|
emit_binary_expression(expression)
|
|
@@ -181,8 +182,27 @@ module MilkTea
|
|
|
181
182
|
end
|
|
182
183
|
|
|
183
184
|
def variant_equality_helper_name(type)
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
return "mt_struct_eq_#{named_type_c_name(type)}" if type.is_a?(Types::VariantArmPayload)
|
|
186
|
+
|
|
187
|
+
"mt_variant_eq_#{named_type_c_name(type)}"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def struct_equality_expression?(expression)
|
|
191
|
+
EQUALITY_OPERATORS.include?(expression.operator) && struct_equality_type?(expression.left.type)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def struct_equality_type?(type)
|
|
195
|
+
type.is_a?(Types::Struct) && !type.is_a?(Types::Union) && !type.is_a?(Types::VariantArmPayload)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def emit_struct_equality_expression(expression)
|
|
199
|
+
helper_name = struct_equality_helper_name(expression.left.type)
|
|
200
|
+
call = "#{helper_name}(#{emit_expression(expression.left)}, #{emit_expression(expression.right)})"
|
|
201
|
+
expression.operator == "!=" ? "!#{call}" : call
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def struct_equality_helper_name(type)
|
|
205
|
+
"mt_struct_eq_#{named_type_c_name(type)}"
|
|
186
206
|
end
|
|
187
207
|
|
|
188
208
|
def nullable_value_type?(type)
|
|
@@ -298,41 +298,131 @@ module MilkTea
|
|
|
298
298
|
end
|
|
299
299
|
|
|
300
300
|
def uses_variant_equality_helper?
|
|
301
|
-
|
|
301
|
+
!variant_equality_types.empty?
|
|
302
302
|
end
|
|
303
303
|
|
|
304
|
-
def
|
|
305
|
-
|
|
304
|
+
def uses_struct_equality_helper?
|
|
305
|
+
!struct_equality_types.empty?
|
|
306
306
|
end
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
false
|
|
308
|
+
# Aggregate types (structs and variants) compared with ==/!= anywhere in
|
|
309
|
+
# the program, transitively closed over value-typed fields so nested
|
|
310
|
+
# aggregates used inside a comparison also get an equality helper.
|
|
311
|
+
def aggregate_equality_types
|
|
312
|
+
@aggregate_equality_types ||= begin
|
|
313
|
+
types = Set.new
|
|
314
|
+
emitted_functions.each do |function|
|
|
315
|
+
function.body.each do |statement|
|
|
316
|
+
statement_matches_expression_predicate?(
|
|
317
|
+
statement,
|
|
318
|
+
expression_pred: ->(expression) { collect_aggregate_equality_from_expression(expression, types) },
|
|
319
|
+
)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
types
|
|
324
323
|
end
|
|
325
324
|
end
|
|
326
325
|
|
|
327
|
-
def
|
|
328
|
-
|
|
326
|
+
def struct_equality_types
|
|
327
|
+
@struct_equality_types ||= aggregate_equality_types.select { |type| struct_equality_type?(type) || type.is_a?(Types::VariantArmPayload) }
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def variant_equality_types
|
|
331
|
+
@variant_equality_types ||= aggregate_equality_types.select { |type| type.is_a?(Types::Variant) }
|
|
332
|
+
end
|
|
329
333
|
|
|
334
|
+
def aggregate_equality_type?(type)
|
|
335
|
+
struct_equality_type?(type) || type.is_a?(Types::Variant) || type.is_a?(Types::VariantArmPayload)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def collect_aggregate_equality_from_expression(expression, types)
|
|
330
339
|
case expression
|
|
331
340
|
when IR::Binary
|
|
332
|
-
|
|
333
|
-
|
|
341
|
+
collect_aggregate_equality_from_expression(expression.left, types)
|
|
342
|
+
collect_aggregate_equality_from_expression(expression.right, types)
|
|
343
|
+
if EQUALITY_OPERATORS.include?(expression.operator) && aggregate_equality_type?(expression.left.type)
|
|
344
|
+
collect_aggregate_equality_dependencies(expression.left.type, types)
|
|
345
|
+
end
|
|
334
346
|
when IR::Call
|
|
335
|
-
expression.
|
|
347
|
+
collect_aggregate_equality_from_expression(expression.callee, types) unless expression.callee.is_a?(String)
|
|
348
|
+
expression.arguments.each { |argument| collect_aggregate_equality_from_expression(argument, types) }
|
|
349
|
+
when IR::Member
|
|
350
|
+
collect_aggregate_equality_from_expression(expression.receiver, types)
|
|
351
|
+
when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
|
|
352
|
+
collect_aggregate_equality_from_expression(expression.receiver, types)
|
|
353
|
+
collect_aggregate_equality_from_expression(expression.index, types)
|
|
354
|
+
when IR::Unary
|
|
355
|
+
collect_aggregate_equality_from_expression(expression.operand, types)
|
|
356
|
+
when IR::Conditional
|
|
357
|
+
collect_aggregate_equality_from_expression(expression.condition, types)
|
|
358
|
+
collect_aggregate_equality_from_expression(expression.then_expression, types)
|
|
359
|
+
collect_aggregate_equality_from_expression(expression.else_expression, types)
|
|
360
|
+
when IR::ReinterpretExpr, IR::Cast, IR::AddressOf
|
|
361
|
+
collect_aggregate_equality_from_expression(expression.expression, types)
|
|
362
|
+
when IR::AggregateLiteral
|
|
363
|
+
expression.fields.each { |field| collect_aggregate_equality_from_expression(field.value, types) }
|
|
364
|
+
when IR::ArrayLiteral
|
|
365
|
+
expression.elements.each { |element| collect_aggregate_equality_from_expression(element, types) }
|
|
366
|
+
when IR::VariantLiteral
|
|
367
|
+
expression.fields.each { |field| collect_aggregate_equality_from_expression(field.value, types) }
|
|
368
|
+
end
|
|
369
|
+
false
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def collect_aggregate_equality_dependencies(type, types)
|
|
373
|
+
return if types.include?(type)
|
|
374
|
+
|
|
375
|
+
case type
|
|
376
|
+
when Types::Variant
|
|
377
|
+
types << type
|
|
378
|
+
type.arm_names.each do |arm_name|
|
|
379
|
+
(type.arm(arm_name) || {}).each_value { |field_type| collect_value_field_equality_dependencies(field_type, types) }
|
|
380
|
+
end
|
|
381
|
+
when Types::VariantArmPayload
|
|
382
|
+
types << type
|
|
383
|
+
arm_fields = type.variant_type.arm(type.arm_name) || {}
|
|
384
|
+
arm_fields.each_value { |field_type| collect_value_field_equality_dependencies(field_type, types) }
|
|
385
|
+
when Types::Struct
|
|
386
|
+
return unless struct_equality_type?(type)
|
|
387
|
+
|
|
388
|
+
types << type
|
|
389
|
+
type.fields.each_value { |field_type| collect_value_field_equality_dependencies(field_type, types) }
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def collect_value_field_equality_dependencies(type, types)
|
|
394
|
+
case type
|
|
395
|
+
when Types::Nullable
|
|
396
|
+
collect_value_field_equality_dependencies(type.base, types)
|
|
397
|
+
when Types::Struct, Types::Variant
|
|
398
|
+
collect_aggregate_equality_dependencies(type, types)
|
|
399
|
+
when Types::VariantArmPayload
|
|
400
|
+
collect_aggregate_equality_dependencies(type, types)
|
|
401
|
+
when Types::GenericInstance
|
|
402
|
+
collect_value_field_equality_dependencies(array_element_type(type), types) if array_type?(type)
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def aggregate_equality_needs_string_view?
|
|
407
|
+
aggregate_equality_types.any? { |type| aggregate_contains_string_view?(type) }
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def aggregate_contains_string_view?(type, visiting = nil)
|
|
411
|
+
return false unless type
|
|
412
|
+
return false if visiting&.key?(type)
|
|
413
|
+
|
|
414
|
+
visiting = (visiting || {}).merge(type => true)
|
|
415
|
+
case type
|
|
416
|
+
when Types::StringView
|
|
417
|
+
true
|
|
418
|
+
when Types::Struct
|
|
419
|
+
type.fields.any? { |_name, field_type| aggregate_contains_string_view?(field_type, visiting) }
|
|
420
|
+
when Types::Variant
|
|
421
|
+
type.arm_names.any? { |arm_name| (type.arm(arm_name) || {}).any? { |_field_name, field_type| aggregate_contains_string_view?(field_type, visiting) } }
|
|
422
|
+
when Types::Nullable
|
|
423
|
+
aggregate_contains_string_view?(type.base, visiting)
|
|
424
|
+
when Types::GenericInstance
|
|
425
|
+
type.arguments.any? { |argument| aggregate_contains_string_view?(argument, visiting) }
|
|
336
426
|
else
|
|
337
427
|
false
|
|
338
428
|
end
|
|
@@ -64,7 +64,10 @@ module MilkTea
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def emit_variant_equality_helpers
|
|
67
|
-
emitted_aggregate_variants.
|
|
67
|
+
variant_decls_by_linkage = (emitted_aggregate_variants + collect_generic_variant_decls).each_with_object({}) { |decl, map| map[decl.linkage_name] = decl }
|
|
68
|
+
variant_equality_types
|
|
69
|
+
.filter_map { |type| variant_decls_by_linkage[named_type_c_name(type)] }
|
|
70
|
+
.flat_map { |variant_decl| emit_variant_equality_helper(variant_decl) }
|
|
68
71
|
end
|
|
69
72
|
|
|
70
73
|
def emit_variant_equality_helper(variant_decl)
|
|
@@ -79,25 +82,14 @@ module MilkTea
|
|
|
79
82
|
lines << "#{INDENT * 3}return true;"
|
|
80
83
|
else
|
|
81
84
|
arm.fields.each do |field|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if c_backend_pointer_like_type?(field_type.base)
|
|
91
|
-
lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
|
|
92
|
-
else
|
|
93
|
-
lines << "#{INDENT * 3}if (#{left_expr}.has_value != #{right_expr}.has_value) return false;"
|
|
94
|
-
lines << "#{INDENT * 3}if (#{left_expr}.has_value && #{left_expr}.value != #{right_expr}.value) return false;"
|
|
95
|
-
end
|
|
96
|
-
elsif field_type.is_a?(Types::Primitive) || field_type.is_a?(Types::EnumBase)
|
|
97
|
-
lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
|
|
98
|
-
else
|
|
99
|
-
lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
|
|
100
|
-
end
|
|
85
|
+
emit_field_equality_guards(
|
|
86
|
+
lines,
|
|
87
|
+
"left.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}",
|
|
88
|
+
"right.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}",
|
|
89
|
+
field.type,
|
|
90
|
+
outer_c:,
|
|
91
|
+
indent: 3,
|
|
92
|
+
)
|
|
101
93
|
end
|
|
102
94
|
lines << "#{INDENT * 3}return true;"
|
|
103
95
|
end
|
|
@@ -109,6 +101,83 @@ module MilkTea
|
|
|
109
101
|
lines
|
|
110
102
|
end
|
|
111
103
|
|
|
104
|
+
def emit_struct_equality_helpers
|
|
105
|
+
struct_decls_by_linkage = (emitted_aggregate_structs + collect_generic_struct_decls).each_with_object({}) { |decl, map| map[decl.linkage_name] = decl }
|
|
106
|
+
struct_equality_types
|
|
107
|
+
.filter_map { |type| type.is_a?(Types::VariantArmPayload) ? type : struct_decls_by_linkage[named_type_c_name(type)] }
|
|
108
|
+
.flat_map { |decl_or_type| emit_struct_equality_helper(decl_or_type) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def emit_struct_equality_helper(struct_decl_or_type)
|
|
112
|
+
if struct_decl_or_type.is_a?(IR::StructDecl)
|
|
113
|
+
outer_c = struct_decl_or_type.linkage_name
|
|
114
|
+
fields = struct_decl_or_type.fields
|
|
115
|
+
else
|
|
116
|
+
payload = struct_decl_or_type
|
|
117
|
+
outer_c = named_type_c_name(payload)
|
|
118
|
+
arm_fields = payload.variant_type.arm(payload.arm_name) || {}
|
|
119
|
+
fields = arm_fields.map { |name, field_type| IR::Field.new(name:, type: field_type) }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
lines = ["static bool mt_struct_eq_#{outer_c}(struct #{outer_c} left, struct #{outer_c} right) {"]
|
|
123
|
+
fields.each do |field|
|
|
124
|
+
emit_field_equality_guards(
|
|
125
|
+
lines,
|
|
126
|
+
"left.#{sanitize_c_identifier(field.name)}",
|
|
127
|
+
"right.#{sanitize_c_identifier(field.name)}",
|
|
128
|
+
field.type,
|
|
129
|
+
outer_c:,
|
|
130
|
+
)
|
|
131
|
+
end
|
|
132
|
+
lines << "#{INDENT}return true;"
|
|
133
|
+
lines << "}"
|
|
134
|
+
lines
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def emit_field_equality_guards(lines, left, right, type, outer_c: nil, indent: 1)
|
|
138
|
+
pad = INDENT * indent
|
|
139
|
+
case type
|
|
140
|
+
when Types::StringView
|
|
141
|
+
lines << "#{pad}if (!mt_str_equal(#{left}, #{right})) return false;"
|
|
142
|
+
when Types::Variant
|
|
143
|
+
if aggregate_field_creates_cycle?(type, outer_c)
|
|
144
|
+
lines << "#{pad}if (#{left} != #{right}) return false;"
|
|
145
|
+
else
|
|
146
|
+
lines << "#{pad}if (!mt_variant_eq_#{named_type_c_name(type)}(#{left}, #{right})) return false;"
|
|
147
|
+
end
|
|
148
|
+
when Types::VariantArmPayload
|
|
149
|
+
if aggregate_field_creates_cycle?(type.variant_type, outer_c)
|
|
150
|
+
lines << "#{pad}if (#{left} != #{right}) return false;"
|
|
151
|
+
else
|
|
152
|
+
lines << "#{pad}if (!mt_struct_eq_#{named_type_c_name(type)}(#{left}, #{right})) return false;"
|
|
153
|
+
end
|
|
154
|
+
when Types::Struct
|
|
155
|
+
if aggregate_field_creates_cycle?(type, outer_c)
|
|
156
|
+
lines << "#{pad}if (#{left} != #{right}) return false;"
|
|
157
|
+
else
|
|
158
|
+
lines << "#{pad}if (!mt_struct_eq_#{named_type_c_name(type)}(#{left}, #{right})) return false;"
|
|
159
|
+
end
|
|
160
|
+
when Types::Nullable
|
|
161
|
+
if c_backend_pointer_like_type?(type.base)
|
|
162
|
+
lines << "#{pad}if (#{left} != #{right}) return false;"
|
|
163
|
+
else
|
|
164
|
+
lines << "#{pad}if (#{left}.has_value != #{right}.has_value) return false;"
|
|
165
|
+
lines << "#{pad}if (#{left}.has_value) {"
|
|
166
|
+
emit_field_equality_guards(lines, "#{left}.value", "#{right}.value", type.base, outer_c:, indent: indent + 1)
|
|
167
|
+
lines << "#{pad}}"
|
|
168
|
+
end
|
|
169
|
+
else
|
|
170
|
+
if array_type?(type)
|
|
171
|
+
count = array_length(type)
|
|
172
|
+
lines << "#{pad}for (uintptr_t index = 0; index < #{count}; index++) {"
|
|
173
|
+
emit_field_equality_guards(lines, "#{left}[index]", "#{right}[index]", array_element_type(type), outer_c:, indent: indent + 1)
|
|
174
|
+
lines << "#{pad}}"
|
|
175
|
+
else
|
|
176
|
+
lines << "#{pad}if (#{left} != #{right}) return false;"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
112
181
|
def emit_async_memory_helpers
|
|
113
182
|
[
|
|
114
183
|
"#define MT_ASYNC_HEADER_SIZE (sizeof(uint64_t) + sizeof(uintptr_t))",
|