mt-lang 0.3.30 → 0.3.31
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/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/lowering/declarations.rb +8 -0
- 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
- 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: 16833c6b95c7e580f12199a381802f47ac628b7252f4bc1647714098cbd4f824
|
|
4
|
+
data.tar.gz: 296b0547096ceee9d394edc13df6f042e2b8ed6d42c98b4745e1b39885407b85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '080aa9b4ad981dce767373642fe8c81fef77bf4b4c364b60e2b5dc633e7e1a2f593187ebcc3903a15aebec587bd6989a844dfd8fd842fb8b61fc855a5ba9ce86'
|
|
7
|
+
data.tar.gz: b9c60e1298c8fb8195401875f3d77358965741c1537cb674696d71ca1b5ef8f8ce5289d7a4e3491bbca24bd485481d6fd65df531a975237c08da888539c30b9e
|
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
|
@@ -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))",
|
|
@@ -236,17 +236,45 @@ module MilkTea
|
|
|
236
236
|
lines << ""
|
|
237
237
|
end
|
|
238
238
|
|
|
239
|
+
if uses_variant_equality_helper? || uses_struct_equality_helper?
|
|
240
|
+
lines.concat(emit_aggregate_equality_forward_declarations)
|
|
241
|
+
lines << ""
|
|
242
|
+
end
|
|
239
243
|
if uses_variant_equality_helper?
|
|
240
244
|
unless uses_str_equality_helper?
|
|
241
|
-
|
|
242
|
-
if variant_needs_str_eq
|
|
243
|
-
lines.concat(emit_string_type) unless uses_string_view?
|
|
244
|
-
lines.concat(emit_str_equality_helper)
|
|
245
|
-
end
|
|
245
|
+
emit_str_equality_support(lines) if aggregate_equality_needs_string_view?
|
|
246
246
|
end
|
|
247
247
|
lines.concat(emit_variant_equality_helpers)
|
|
248
248
|
lines << ""
|
|
249
249
|
end
|
|
250
|
+
if uses_struct_equality_helper?
|
|
251
|
+
unless uses_str_equality_helper?
|
|
252
|
+
emit_str_equality_support(lines) if aggregate_equality_needs_string_view?
|
|
253
|
+
end
|
|
254
|
+
lines.concat(emit_struct_equality_helpers)
|
|
255
|
+
lines << ""
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def emit_aggregate_equality_forward_declarations
|
|
260
|
+
lines = []
|
|
261
|
+
struct_equality_types.each do |type|
|
|
262
|
+
c = named_type_c_name(type)
|
|
263
|
+
lines << "static bool mt_struct_eq_#{c}(struct #{c} left, struct #{c} right);"
|
|
264
|
+
end
|
|
265
|
+
variant_equality_types.each do |type|
|
|
266
|
+
c = named_type_c_name(type)
|
|
267
|
+
lines << "static bool mt_variant_eq_#{c}(struct #{c} left, struct #{c} right);"
|
|
268
|
+
end
|
|
269
|
+
lines
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def emit_str_equality_support(lines)
|
|
273
|
+
return if @emitted_aggregate_str_equality_support
|
|
274
|
+
|
|
275
|
+
@emitted_aggregate_str_equality_support = true
|
|
276
|
+
lines.concat(emit_string_type) unless uses_string_view?
|
|
277
|
+
lines.concat(emit_str_equality_helper)
|
|
250
278
|
end
|
|
251
279
|
|
|
252
280
|
def emit_function_forward_declarations(lines)
|
|
@@ -31,10 +31,35 @@ module MilkTea
|
|
|
31
31
|
return left == right if left.is_a?(String) && right.is_a?(String)
|
|
32
32
|
return left == right if boolean_value?(left) && boolean_value?(right)
|
|
33
33
|
return left == right if left.is_a?(Types::Base) && right.is_a?(Types::Base)
|
|
34
|
+
return struct_equality_result(left, right) if left.is_a?(Hash) && right.is_a?(Hash)
|
|
35
|
+
return variant_equality_result(left, right) if left.is_a?(VariantValue) && right.is_a?(VariantValue)
|
|
34
36
|
|
|
35
37
|
nil
|
|
36
38
|
end
|
|
37
39
|
|
|
40
|
+
# Const-time struct values are represented as {field_name => value} hashes;
|
|
41
|
+
# compare them field by field, mirroring the runtime struct == semantics.
|
|
42
|
+
def self.struct_equality_result(left, right)
|
|
43
|
+
return nil unless left.is_a?(Hash) && right.is_a?(Hash)
|
|
44
|
+
return nil unless left.keys.sort == right.keys.sort
|
|
45
|
+
|
|
46
|
+
left.each do |name, value|
|
|
47
|
+
field_result = equality_result(value, right[name])
|
|
48
|
+
return nil if field_result.nil?
|
|
49
|
+
return false if field_result == false
|
|
50
|
+
end
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Const-time variant values; fields mirror the runtime arm payload layout.
|
|
55
|
+
VariantValue = Data.define(:arm, :fields)
|
|
56
|
+
|
|
57
|
+
def self.variant_equality_result(left, right)
|
|
58
|
+
return false unless left.arm == right.arm
|
|
59
|
+
|
|
60
|
+
struct_equality_result(left.fields, right.fields)
|
|
61
|
+
end
|
|
62
|
+
|
|
38
63
|
def self.boolean_value?(value)
|
|
39
64
|
value == true || value == false
|
|
40
65
|
end
|
|
@@ -78,6 +78,14 @@ module MilkTea
|
|
|
78
78
|
IR::AggregateField.new(name:, value: lower_const_value_literal(field_type, field_value))
|
|
79
79
|
end
|
|
80
80
|
IR::AggregateLiteral.new(type:, fields:)
|
|
81
|
+
when CompileTime::VariantValue
|
|
82
|
+
arm_field_types = type.respond_to?(:arm) ? (type.arm(const_value.arm) || {}) : {}
|
|
83
|
+
fields = const_value.fields.map do |field_name, field_value|
|
|
84
|
+
field_type = arm_field_types[field_name]
|
|
85
|
+
raise LoweringError.new("constant variant arm #{const_value.arm} field #{field_name} not found in #{type}", line: 0, column: 0, path: @ctx.current_analysis_path) unless field_type
|
|
86
|
+
IR::AggregateField.new(name: field_name, value: lower_const_value_literal(field_type, field_value))
|
|
87
|
+
end
|
|
88
|
+
IR::VariantLiteral.new(type:, arm_name: const_value.arm, fields:)
|
|
81
89
|
else
|
|
82
90
|
raise LoweringError.new("unsupported const value type #{const_value.class}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
83
91
|
end
|
|
@@ -270,10 +270,19 @@ module MilkTea
|
|
|
270
270
|
|
|
271
271
|
if expected_type.is_a?(Types::Primitive) && expected_type.integer? &&
|
|
272
272
|
value_fits_integer_type?(expression.value, expected_type)
|
|
273
|
-
expected_type
|
|
274
|
-
else
|
|
275
|
-
@ctx.types.fetch("int")
|
|
273
|
+
return expected_type
|
|
276
274
|
end
|
|
275
|
+
|
|
276
|
+
# A bare literal (no suffix, no fitting expected type) picks the
|
|
277
|
+
# narrowest fixed-width type that holds its value: int -> long -> ulong.
|
|
278
|
+
# Falling through to int unconditionally emitted overflowing C
|
|
279
|
+
# (e.g. `int32_t x = 2147483648;`).
|
|
280
|
+
["int", "long", "ulong"].each do |name|
|
|
281
|
+
candidate = @ctx.types.fetch(name)
|
|
282
|
+
return candidate if value_fits_integer_type?(expression.value, candidate)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
raise_sema_error("integer literal #{expression.value} does not fit in any integer type", expression)
|
|
277
286
|
end
|
|
278
287
|
|
|
279
288
|
INTEGER_SUFFIX_TYPES = {
|
|
@@ -300,11 +309,23 @@ module MilkTea
|
|
|
300
309
|
@ctx.types.fetch("double")
|
|
301
310
|
elsif expected_type.is_a?(Types::Primitive) && expected_type.float?
|
|
302
311
|
expected_type
|
|
303
|
-
|
|
312
|
+
elsif float_literal_fits_in_float?(expression.value)
|
|
304
313
|
@ctx.types.fetch("float")
|
|
314
|
+
else
|
|
315
|
+
# A bare float literal beyond float32 range would silently overflow
|
|
316
|
+
# to infinity in C (`float x = 1e40`); promote to double instead.
|
|
317
|
+
@ctx.types.fetch("double")
|
|
305
318
|
end
|
|
306
319
|
end
|
|
307
320
|
|
|
321
|
+
FLOAT32_MAX_MAGNITUDE = 3.4028234663852886e+38
|
|
322
|
+
|
|
323
|
+
def float_literal_fits_in_float?(value)
|
|
324
|
+
return false unless value.is_a?(Numeric) && value.finite?
|
|
325
|
+
|
|
326
|
+
value.abs <= FLOAT32_MAX_MAGNITUDE
|
|
327
|
+
end
|
|
328
|
+
|
|
308
329
|
def infer_identifier(expression, scopes:, expected_type: nil)
|
|
309
330
|
binding = lookup_value(expression.name, scopes)
|
|
310
331
|
if binding
|
|
@@ -662,8 +683,16 @@ module MilkTea
|
|
|
662
683
|
when "==", "!="
|
|
663
684
|
unless c_natively_equality_comparable_type?(left_type) && c_natively_equality_comparable_type?(right_type)
|
|
664
685
|
bad_type = c_natively_equality_comparable_type?(right_type) ? left_type : right_type
|
|
665
|
-
if
|
|
666
|
-
raise_sema_error("operator #{expression.operator} is not supported for
|
|
686
|
+
if bad_type.is_a?(Types::Union)
|
|
687
|
+
raise_sema_error("operator #{expression.operator} is not supported for union type #{bad_type}; use equal[#{bad_type}](...) instead")
|
|
688
|
+
elsif bad_type.is_a?(Types::Variant)
|
|
689
|
+
field = first_non_equality_comparable_variant_field(bad_type)
|
|
690
|
+
field_hint = field ? " (arm '#{field[0]}' field '#{field[1]}' of type #{field[2]} is not equality-comparable)" : ""
|
|
691
|
+
raise_sema_error("operator #{expression.operator} is not supported for variant type #{bad_type}#{field_hint}; use equal[#{bad_type}](...) instead")
|
|
692
|
+
elsif struct_instance_type?(bad_type) && !bad_type.is_a?(Types::Variant)
|
|
693
|
+
field = first_non_equality_comparable_field(bad_type)
|
|
694
|
+
field_hint = field ? " (field '#{field.first}' of type #{field.last} is not equality-comparable)" : ""
|
|
695
|
+
raise_sema_error("operator #{expression.operator} is not supported for struct type #{bad_type}#{field_hint}; use equal[#{bad_type}](...) instead")
|
|
667
696
|
else
|
|
668
697
|
raise_sema_error("operator #{expression.operator} is not supported for type #{bad_type}")
|
|
669
698
|
end
|
|
@@ -902,23 +902,65 @@ module MilkTea
|
|
|
902
902
|
type.is_a?(Types::Struct) || type.is_a?(Types::Variant)
|
|
903
903
|
end
|
|
904
904
|
|
|
905
|
-
def c_natively_equality_comparable_type?(type)
|
|
905
|
+
def c_natively_equality_comparable_type?(type, visiting = nil)
|
|
906
906
|
return true if type.is_a?(Types::Primitive)
|
|
907
907
|
return true if type.is_a?(Types::EnumBase)
|
|
908
908
|
return true if type.is_a?(Types::Opaque)
|
|
909
|
-
return true if type.is_a?(Types::Nullable)
|
|
909
|
+
return true if type.is_a?(Types::Nullable) && equality_comparable_field_type?(type.base, visiting)
|
|
910
910
|
return true if type.is_a?(Types::Null)
|
|
911
911
|
return true if type.is_a?(Types::Function)
|
|
912
912
|
return true if type.is_a?(Types::Error)
|
|
913
913
|
return true if type.is_a?(Types::StringView)
|
|
914
914
|
return true if pointer_type?(type)
|
|
915
915
|
return true if ref_type?(type)
|
|
916
|
-
return true if type.is_a?(Types::Variant)
|
|
916
|
+
return true if type.is_a?(Types::Variant) && equality_comparable_variant_type?(type, visiting)
|
|
917
917
|
return true if type.is_a?(Types::VariantArmPayload)
|
|
918
|
+
return true if equality_comparable_struct_type?(type, visiting)
|
|
919
|
+
|
|
920
|
+
false
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
# A struct value is equality-comparable with ==/!= when every field is
|
|
924
|
+
# itself equality-comparable. Untagged unions are excluded because their
|
|
925
|
+
# active-field comparison is ambiguous.
|
|
926
|
+
def equality_comparable_struct_type?(type, visiting = nil)
|
|
927
|
+
type.is_a?(Types::Struct) && !type.is_a?(Types::Union) &&
|
|
928
|
+
type.fields.all? { |_name, field_type| equality_comparable_field_type?(field_type, visiting) }
|
|
929
|
+
end
|
|
930
|
+
|
|
931
|
+
def equality_comparable_field_type?(type, visiting = nil)
|
|
932
|
+
return true if c_natively_equality_comparable_type?(type, visiting)
|
|
933
|
+
return true if array_type?(type) && equality_comparable_field_type?(array_element_type(type), visiting)
|
|
918
934
|
|
|
919
935
|
false
|
|
920
936
|
end
|
|
921
937
|
|
|
938
|
+
# A variant is equality-comparable when every arm payload field is itself
|
|
939
|
+
# equality-comparable. A variant that reaches itself through value fields
|
|
940
|
+
# (a recursive/cyclic variant) is comparable too: the C backend embeds
|
|
941
|
+
# cyclic fields as pointers, and those fields compare by pointer identity.
|
|
942
|
+
def equality_comparable_variant_type?(type, visiting = nil)
|
|
943
|
+
return true if visiting&.key?(type)
|
|
944
|
+
|
|
945
|
+
visiting = (visiting || {}).merge(type => true)
|
|
946
|
+
type.arm_names.all? do |arm_name|
|
|
947
|
+
(type.arm(arm_name) || {}).all? { |_field_name, field_type| equality_comparable_field_type?(field_type, visiting) }
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
def first_non_equality_comparable_field(struct_type)
|
|
952
|
+
struct_type.fields.find { |_name, field_type| !equality_comparable_field_type?(field_type) }
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
def first_non_equality_comparable_variant_field(variant_type)
|
|
956
|
+
variant_type.arm_names.each do |arm_name|
|
|
957
|
+
(variant_type.arm(arm_name) || {}).each do |field_name, field_type|
|
|
958
|
+
return [arm_name, field_name, field_type] unless equality_comparable_field_type?(field_type)
|
|
959
|
+
end
|
|
960
|
+
end
|
|
961
|
+
nil
|
|
962
|
+
end
|
|
963
|
+
|
|
922
964
|
def collection_loop_type(type)
|
|
923
965
|
super
|
|
924
966
|
end
|
|
@@ -301,7 +301,12 @@ module MilkTea
|
|
|
301
301
|
end
|
|
302
302
|
|
|
303
303
|
if (receiver_type = resolve_type_expression(member_access_expression.receiver))
|
|
304
|
-
|
|
304
|
+
if receiver_type.is_a?(Types::EnumBase)
|
|
305
|
+
next resolve_enum_member_const_value(receiver_type, member_access_expression.member)
|
|
306
|
+
end
|
|
307
|
+
if receiver_type.is_a?(Types::Variant)
|
|
308
|
+
next CompileTime::VariantValue.new(arm: member_access_expression.member, fields: {})
|
|
309
|
+
end
|
|
305
310
|
end
|
|
306
311
|
|
|
307
312
|
next unless member_access_expression.receiver.is_a?(AST::Identifier)
|
|
@@ -321,6 +326,31 @@ module MilkTea
|
|
|
321
326
|
|
|
322
327
|
def evaluate_compile_time_call(expression, scopes: nil)
|
|
323
328
|
case expression.callee
|
|
329
|
+
when AST::MemberAccess
|
|
330
|
+
if (receiver_type = resolve_type_expression(expression.callee.receiver)) && receiver_type.is_a?(Types::Variant)
|
|
331
|
+
arm_name = expression.callee.member
|
|
332
|
+
fields = {}
|
|
333
|
+
expression.arguments.each do |argument|
|
|
334
|
+
val = CompileTime.evaluate(argument.value, resolve_identifier: lambda { |id|
|
|
335
|
+
if scopes
|
|
336
|
+
binding = lookup_value(id.name, scopes)
|
|
337
|
+
return binding.const_value unless binding&.const_value.nil?
|
|
338
|
+
end
|
|
339
|
+
resolve_current_module_const_value(id.name)
|
|
340
|
+
}, resolve_member_access: lambda { |ma|
|
|
341
|
+
if (member_receiver_type = resolve_type_expression(ma.receiver))
|
|
342
|
+
next resolve_enum_member_const_value(member_receiver_type, ma.member) if member_receiver_type.is_a?(Types::EnumBase)
|
|
343
|
+
next CompileTime::VariantValue.new(arm: ma.member, fields: {}) if member_receiver_type.is_a?(Types::Variant)
|
|
344
|
+
end
|
|
345
|
+
nil
|
|
346
|
+
}, resolve_call: lambda { |inner_call|
|
|
347
|
+
evaluate_compile_time_call(inner_call, scopes:)
|
|
348
|
+
})
|
|
349
|
+
return nil unless val
|
|
350
|
+
fields[argument.name] = val
|
|
351
|
+
end
|
|
352
|
+
return CompileTime::VariantValue.new(arm: arm_name, fields: fields)
|
|
353
|
+
end
|
|
324
354
|
when AST::Identifier
|
|
325
355
|
if (struct_type = @ctx.types[expression.callee.name]) && struct_type.is_a?(Types::Struct)
|
|
326
356
|
fields = {}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mt-lang
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.31
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Long (Teefan) Tran
|
|
@@ -624,7 +624,7 @@ metadata:
|
|
|
624
624
|
homepage_uri: https://teefan.github.io/mt-lang/
|
|
625
625
|
source_code_uri: https://github.com/teefan/mt-lang
|
|
626
626
|
post_install_message: |
|
|
627
|
-
Milk Tea 0.3.
|
|
627
|
+
Milk Tea 0.3.31 installed!
|
|
628
628
|
|
|
629
629
|
System requirements:
|
|
630
630
|
- A C compiler (gcc or clang) must be available on PATH
|