mt-lang 0.3.42 → 0.3.43
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 +5 -2
- data/docs/index.html +20 -4
- data/docs/language-design.md +5 -2
- data/docs/language-manual.md +28 -5
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/c_backend/feature_detection.rb +6 -1
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +30 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +74 -0
- data/lib/milk_tea/core/c_backend.rb +9 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +2 -0
- data/lib/milk_tea/core/lowering/expressions.rb +31 -6
- data/lib/milk_tea/core/lowering/proc.rb +3 -0
- data/lib/milk_tea/core/lowering/resolve.rb +3 -0
- data/lib/milk_tea/core/lowering/utils.rb +26 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +13 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +46 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1 -0
- data/lib/milk_tea/core/types/registry.rb +39 -16
- data/lib/milk_tea/lsp/server/hover.rb +0 -1
- data/lib/milk_tea/lsp/server/text_documents.rb +1 -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: 6842b1cfb6eac5181fdbcd31cd90dbb9f436bb0f3c7b0cc25d49a8c1f8e13a8b
|
|
4
|
+
data.tar.gz: 4e97091f3da86a115656c6ec7d9bb10fe5cd65360bd59a203fc6595111285aff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71630831d0eceda79fefdd93b07a9f83b48c3ef3b0cd879c5c7db8b064e459ef321d36b49a2e0d46e8ba20f134fffc65039cdf455d3cc47638ea031c3edddd83
|
|
7
|
+
data.tar.gz: b7fccbe3f6547fcb7e17c07b9002d278965934a0f0ec68d1b6c79a1ce5b48d807186633a9e57b75e34c8671413ed984a18823a575719ba1f8af4e3777c3309f3
|
data/README.md
CHANGED
|
@@ -746,6 +746,7 @@ Postfix forms:
|
|
|
746
746
|
|
|
747
747
|
- member access: `a.b`
|
|
748
748
|
- indexing: `a[i]`
|
|
749
|
+
- range index: `a[start..stop]` — `str` yields a borrowed `str` view; `array[T, N]` and `span[T]` yield a borrowed `span[T]` view; half-open `[start, stop)`, no copy, no allocation
|
|
749
750
|
- call: `f(x)`
|
|
750
751
|
- partial field update: `v.with(x = 10.0)` — returns a copy with specified fields replaced
|
|
751
752
|
- specialization: `name[T]`, `name[32]`, `mod.name[T]`
|
|
@@ -939,7 +940,7 @@ See module source for full method surface. Iterator forms:
|
|
|
939
940
|
|
|
940
941
|
Text categories:
|
|
941
942
|
|
|
942
|
-
- `str` -> string view. The `+` operator concatenates two `str` values into a per-thread scratch buffer (no heap allocation), returning a borrowed `str`; the result stays valid while cumulative concatenations on that thread remain within the scratch buffer budget. For loops or repeated concatenation, prefer `string.String` for amortized building.
|
|
943
|
+
- `str` -> string view. `s[i]` reads the byte at `i` as `ubyte` (bounds-checked). `s[start..stop]` returns a borrowed `str` view using byte offsets; bounds must be UTF-8 code-unit boundaries or the slice traps. The `+` operator concatenates two `str` values into a per-thread scratch buffer (no heap allocation), returning a borrowed `str`; the result stays valid while cumulative concatenations on that thread remain within the scratch buffer budget. For loops or repeated concatenation, prefer `string.String` for amortized building.
|
|
943
944
|
- `cstr` -> C ABI string
|
|
944
945
|
- `str_buffer[N]` -> fixed-capacity mutable UTF-8 text buffer
|
|
945
946
|
|
|
@@ -989,6 +990,7 @@ Heredoc notes:
|
|
|
989
990
|
- Shift operators require integer operands.
|
|
990
991
|
- Safe array indexing requires an addressable array value.
|
|
991
992
|
- Safe indexing (`arr[i]`) is bounds-checked and calls `fatal` on out-of-bounds access.
|
|
993
|
+
- Range indexing (`arr[start..stop]`, `span[start..stop]`, `str[start..stop]`) returns a borrowed view: `span[T]` for arrays and spans, `str` for strings. Bounds may be any integer types (converted to `ptr_uint`); `start > stop` or `stop > len` traps at runtime. Array range slices require an addressable array value; str slices must land on UTF-8 code-unit boundaries. Bounds-checked with no copy or allocation.
|
|
992
994
|
- Use `get(arr, i)` for recoverable indexing that returns `ptr[T]?` (null on out-of-bounds) instead of aborting.
|
|
993
995
|
- Pointer indexing requires `unsafe`.
|
|
994
996
|
- `read(ptr)` requires `unsafe`.
|
|
@@ -1145,7 +1147,8 @@ Current compiler rejects:
|
|
|
1145
1147
|
|
|
1146
1148
|
- `+` concatenates `str`; `cstr` and mixed `str`/`cstr` concatenation are not supported
|
|
1147
1149
|
- `==`/`!=` on structs and variants requires all fields equality-comparable; use `equal[T]` otherwise
|
|
1148
|
-
- range expressions are restricted to `for`-loop iterables and range-index assignment targets
|
|
1150
|
+
- range expressions are restricted to `for`-loop iterables, range index reads (`a[start..stop]`), and range-index assignment targets
|
|
1151
|
+
- `str` is an immutable borrowed view: index and range-index results cannot be assigned through
|
|
1149
1152
|
- functions, methods, generic functions, and variant arms must be called — they are not usable as bare values
|
|
1150
1153
|
- `read(...)` of a raw pointer requires `unsafe`
|
|
1151
1154
|
- a statement cannot begin with a binary operator (including `+`, `-`, `and`, `or`, `is`, `..`); continuation requires ending the previous line with the operator or wrapping in `()`
|
data/docs/index.html
CHANGED
|
@@ -1262,7 +1262,7 @@ let d: dyn[Drawable] = adapt[Drawable](ref_of(entity))</code></pre>
|
|
|
1262
1262
|
<tr><td><code>ptr_int</code> <code>ptr_uint</code></td><td>Pointer-sized integers</td></tr>
|
|
1263
1263
|
<tr><td><code>float</code> <code>double</code></td><td>Floating-point</td></tr>
|
|
1264
1264
|
<tr><td><code>void</code></td><td>No value</td></tr>
|
|
1265
|
-
<tr><td><code>str</code></td><td>UTF-8 string view (borrowed). The <code>+</code> operator concatenates two <code>str</code> values into a per-thread scratch buffer (no allocation); the result is valid while the scratch budget lasts.</td></tr>
|
|
1265
|
+
<tr><td><code>str</code></td><td>UTF-8 string view (borrowed). <code>s[i]</code> reads the byte at <code>i</code> as <code>ubyte</code> (bounds-checked); <code>s[start..stop]</code> returns a borrowed view using byte offsets and traps off UTF-8 boundaries. The <code>+</code> operator concatenates two <code>str</code> values into a per-thread scratch buffer (no allocation); the result is valid while the scratch budget lasts.</td></tr>
|
|
1266
1266
|
<tr><td><code>cstr</code></td><td>NUL-terminated C string</td></tr>
|
|
1267
1267
|
<tr><td><code>vec2</code> <code>vec3</code> <code>vec4</code></td><td>Float vectors with <code>.x .y .z .w</code></td></tr>
|
|
1268
1268
|
<tr><td><code>ivec2</code> <code>ivec3</code> <code>ivec4</code></td><td>Integer vectors</td></tr>
|
|
@@ -1487,8 +1487,18 @@ else:
|
|
|
1487
1487
|
do_work()</code></pre>
|
|
1488
1488
|
</div>
|
|
1489
1489
|
|
|
1490
|
+
<h3>Range Index</h3>
|
|
1491
|
+
<p>Range-index reads borrow a sub-view with no copy and no allocation. The result is a borrowed <code class="code-inline">str</code> for string receivers and a borrowed <code class="code-inline">span[T]</code> for arrays and spans. Bounds may be any integer types. The range is start-inclusive and end-exclusive. <code>start > stop</code> or <code>stop > len</code> traps at runtime; string slices additionally require UTF-8 code-unit boundaries at both bounds.</p>
|
|
1492
|
+
<div class="code-wrap">
|
|
1493
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1494
|
+
<pre><code>let text: str = "hello world"
|
|
1495
|
+
let head = text[0..5] # str view: "hello"
|
|
1496
|
+
var values: array[int, 4] = (10, 20, 30, 40)
|
|
1497
|
+
let middle = values[1..3] # span[int] view over 20, 30</code></pre>
|
|
1498
|
+
</div>
|
|
1499
|
+
|
|
1490
1500
|
<h3>Range Index Assignment</h3>
|
|
1491
|
-
<p>Assign a tuple to a contiguous slice using an exclusive range with literal bounds. The tuple width must match the slice width exactly.</p>
|
|
1501
|
+
<p>Assign a tuple to a contiguous slice using an exclusive range with literal bounds. The tuple width must match the slice width exactly. String views are immutable and cannot be assigned through.</p>
|
|
1492
1502
|
<div class="code-wrap">
|
|
1493
1503
|
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1494
1504
|
<pre><code>var buf: array[float, 4]
|
|
@@ -1749,6 +1759,10 @@ value.field
|
|
|
1749
1759
|
arr[i]
|
|
1750
1760
|
func(arg)
|
|
1751
1761
|
|
|
1762
|
+
## Range index: str returns str view; arrays and spans return span[T] view
|
|
1763
|
+
let head = text[0..5]
|
|
1764
|
+
let middle = values[1..3]
|
|
1765
|
+
|
|
1752
1766
|
## Partial field update (returns copy)
|
|
1753
1767
|
let moved = v.with(x = 10.0)
|
|
1754
1768
|
|
|
@@ -1871,7 +1885,7 @@ function field_equal[T](a: const_ptr[T], b: const_ptr[T]) -> bool:
|
|
|
1871
1885
|
<div class="table-wrap">
|
|
1872
1886
|
<table class="attr-table">
|
|
1873
1887
|
<tr><th>Type</th><th>Ownership</th><th>Use Case</th></tr>
|
|
1874
|
-
<tr><td><code>str</code></td><td>Borrowed</td><td>Read-only UTF-8 view (literals, format strings, slicing); a stored or returned dynamic format string carries its own heap buffer</td></tr>
|
|
1888
|
+
<tr><td><code>str</code></td><td>Borrowed</td><td>Read-only UTF-8 view (literals, format strings, slicing, <code>x[start..stop]</code>); a stored or returned dynamic format string carries its own heap buffer</td></tr>
|
|
1875
1889
|
<tr><td><code>cstr</code></td><td>Borrowed</td><td>NUL-terminated C ABI string (<code>c"hello"</code>)</td></tr>
|
|
1876
1890
|
<tr><td><code>str_buffer[N]</code></td><td>Owned (stack)</td><td>Fixed-capacity mutable UTF-8 builder</td></tr>
|
|
1877
1891
|
<tr><td><code>std.string.String</code></td><td>Owned (heap)</td><td>Growable owned text via <code>fmt.format(f"...")</code></td></tr>
|
|
@@ -1980,7 +1994,7 @@ MSG</code></pre>
|
|
|
1980
1994
|
<tr><td>Conditions must be <code>bool</code></td><td>No truthy/falsy coercion from integers or pointers</td></tr>
|
|
1981
1995
|
<tr><td>Explicit casts for mixed signed/unsigned</td><td>Mixed signed/unsigned arithmetic requires <code>T<-value</code></td></tr>
|
|
1982
1996
|
<tr><td>Enum/flags don't auto-coerce</td><td>Outside external-call boundaries, no implicit conversion to backing integers</td></tr>
|
|
1983
|
-
<tr><td>Safe indexing is bounds-checked</td><td><code>arr[i]</code>
|
|
1997
|
+
<tr><td>Safe indexing is bounds-checked</td><td><code>arr[i]</code> and <code>a[start..stop]</code> call <code>fatal</code> on OOB; string slices additionally require UTF-8 boundaries; use <code>get(arr, i)</code> for recoverable single-element access</td></tr>
|
|
1984
1998
|
<tr><td>Pointer indexing requires <code>unsafe</code></td><td>Raw pointer dereference, arithmetic, casts, <code>reinterpret</code> all need <code>unsafe</code></td></tr>
|
|
1985
1999
|
<tr><td><code>%</code> requires integer operands</td><td></td></tr>
|
|
1986
2000
|
<tr><td>Bitwise ops require matching types</td><td>Integer or flags types</td></tr>
|
|
@@ -2429,6 +2443,8 @@ import std.math # intentionally available for downstream</code></pre>
|
|
|
2429
2443
|
<li>Enum and flags values do not implicitly coerce to backing integers</li>
|
|
2430
2444
|
<li><code>+</code> concatenates <code>str</code>; <code>cstr</code> and mixed <code>str</code>/<code>cstr</code> are not concatenable</li>
|
|
2431
2445
|
<li><code>==</code>/<code>!=</code> on structs and variants requires all fields equality-comparable; use <code>equal[T]</code> for non-comparable types</li>
|
|
2446
|
+
<li>Range expressions are restricted to <code>for</code>-loop iterables, range-index reads (<code>a[start..stop]</code>), and range-index assignment targets</li>
|
|
2447
|
+
<li><code>str</code> views are immutable borrowed text; index and range-index results cannot be assigned through</li>
|
|
2432
2448
|
<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>
|
|
2433
2449
|
<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>
|
|
2434
2450
|
</ul>
|
data/docs/language-design.md
CHANGED
|
@@ -409,13 +409,15 @@ Rules:
|
|
|
409
409
|
- Parallel `for` accepts multiple array/span iterables and binds them in lockstep.
|
|
410
410
|
- Parallel `for` does not accept ranges, and iterable lengths must match.
|
|
411
411
|
|
|
412
|
+
Range-index reads borrow sub-views with the same syntax. `a[start..stop]` on `array[T, N]` or `span[T]` returns a borrowed `span[T]`; on `str` it returns a borrowed `str`. The range is end-exclusive, bounds may be any integer types, and out-of-bounds or inverted bounds trap at runtime like all safe indexing. These reads copy nothing and allocate nothing — they only form a pointer-plus-length view.
|
|
413
|
+
|
|
412
414
|
Range-index assignment is also part of the control-flow-and-mutation surface when code wants an explicit fixed-width slice update:
|
|
413
415
|
|
|
414
416
|
```mt
|
|
415
417
|
buf[0..3] = (1.0, 2.0, 3.0)
|
|
416
418
|
```
|
|
417
419
|
|
|
418
|
-
The bounds are integer literals, the range is end-exclusive, and the right-hand tuple width must match the slice width exactly.
|
|
420
|
+
The bounds are integer literals, the range is end-exclusive, and the right-hand tuple width must match the slice width exactly. Range-index assignment writes element-wise into array or span storage; `str` views are immutable and cannot be assigned through.
|
|
419
421
|
|
|
420
422
|
### Useful structured features
|
|
421
423
|
|
|
@@ -580,6 +582,7 @@ Notes:
|
|
|
580
582
|
- Fixed-array indexing is bounds-checked and safe by default.
|
|
581
583
|
- Safe array indexing requires an addressable array value; bind temporaries before indexing them.
|
|
582
584
|
- Safe indexing (`arr[i]`) aborts on out-of-bounds via `fatal`. Use `get(arr, i)` for recoverable bounds-checked access that returns `ptr[T]?`.
|
|
585
|
+
- Range indexing (`arr[start..stop]` on arrays and spans) returns a borrowed `span[T]` view; it copies nothing and requires an addressable array value.
|
|
583
586
|
- When a `span[T]` boundary is expected, addressable `array[T, N]` values coerce directly. Arrays also expose `.as_span()` for explicit conversion when the target type is not a call boundary.
|
|
584
587
|
- `array[char, N]` and `span[char]` are the ordinary source-level forms for raw writable character storage and byte-oriented foreign buffers. They are not alternate text objects and should not grow a parallel everyday text API.
|
|
585
588
|
- `str_buffer[N]` is the one source-level mutable UTF-8 text type. It owns `N` writable text bytes plus an implementation-managed trailing NUL slot, tracks current text length, and refreshes that length when a writable buffer alias mutates the underlying storage.
|
|
@@ -592,7 +595,7 @@ Notes:
|
|
|
592
595
|
- `.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.
|
|
593
596
|
- `.capacity()` reports the maximum writable text bytes, not counting the reserved trailing NUL slot.
|
|
594
597
|
- `.as_str()` and `.as_cstr()` borrow from the same builder storage and revalidate through that same dirty-refresh path before returning.
|
|
595
|
-
- `str
|
|
598
|
+
- `str[start..stop]` is the language-level slice surface: it returns a borrowed `str` view using byte offsets with an end-exclusive range, and both bounds must be UTF-8 code-unit boundaries or the slice traps at runtime. `s[i]` reads the byte at `i` as `ubyte`, bounds-checked. `str.slice(start, len)` remains the library spelling for start-plus-length slicing.
|
|
596
599
|
- Ordinary string lists stay `array[str, N]` or `span[str]` in source. If an imported foreign declaration chooses that public surface for a raw `char **`, `span[cstr]`, or pointer-plus-length text-list API, the boundary owns the temporary marshalling.
|
|
597
600
|
- Imported foreign declarations may map `str_buffer[N] as ptr[char]` directly when the public surface wants writable UTF-8 text with fixed caller capacity.
|
|
598
601
|
- If the raw call also needs the caller buffer size, a `str_buffer[N]` public signature should pass `text_public.capacity() + 1` in the foreign mapping so the raw side sees the full writable byte count including the trailing NUL slot.
|
data/docs/language-manual.md
CHANGED
|
@@ -934,22 +934,41 @@ Unsafe context is required for raw-pointer-level operations such as:
|
|
|
934
934
|
- pointer casts
|
|
935
935
|
- `reinterpret[...]`
|
|
936
936
|
|
|
937
|
-
### 4.6 Range
|
|
937
|
+
### 4.6 Range indexing
|
|
938
938
|
|
|
939
|
-
|
|
939
|
+
Range index reads borrow a sub-view with no copy and no allocation:
|
|
940
|
+
|
|
941
|
+
```mt
|
|
942
|
+
let text: str = "hello world"
|
|
943
|
+
let head = text[0..5] # str view: "hello"
|
|
944
|
+
var values: array[int, 4] = (10, 20, 30, 40)
|
|
945
|
+
let middle = values[1..3] # span[int] view over 20, 30
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
Rules for range index reads:
|
|
949
|
+
|
|
950
|
+
- the receiver must be `str`, `array[T, N]`, or `span[T]`
|
|
951
|
+
- the result is a borrowed view: `str` for string receivers, `span[T]` for arrays and spans
|
|
952
|
+
- the range is start-inclusive and end-exclusive; bounds may be any integer types
|
|
953
|
+
- `start > stop` or `stop > len` traps at runtime
|
|
954
|
+
- array receivers require an addressable array value; literal bounds are checked at compile time
|
|
955
|
+
- `str` slices use byte offsets and both bounds must be UTF-8 code-unit boundaries or the slice traps
|
|
956
|
+
|
|
957
|
+
Range index assignment writes elements in place:
|
|
940
958
|
|
|
941
959
|
```mt
|
|
942
960
|
var buf: array[float, 4]
|
|
943
961
|
buf[0..3] = (1.0, 2.0, 3.0)
|
|
944
962
|
```
|
|
945
963
|
|
|
946
|
-
Rules:
|
|
964
|
+
Rules for range index assignment:
|
|
947
965
|
|
|
948
966
|
- the target must be an addressable array-, span-, or pointer-indexable lvalue
|
|
949
967
|
- the index must be a range expression with integer literal bounds
|
|
950
968
|
- the range is start-inclusive and end-exclusive
|
|
951
969
|
- the right-hand side must be an expression list whose length exactly matches the range width
|
|
952
970
|
- each element must be assignable to the indexed element type
|
|
971
|
+
- `str` receivers cannot be assigned through; str views are immutable
|
|
953
972
|
|
|
954
973
|
### 4.7 When (compile-time conditional)
|
|
955
974
|
|
|
@@ -1051,6 +1070,7 @@ Rules:
|
|
|
1051
1070
|
|
|
1052
1071
|
- member access: `a.b`
|
|
1053
1072
|
- indexing: `a[i]`
|
|
1073
|
+
- range index: `a[start..stop]` — `str` yields a borrowed `str` view; `array[T, N]` and `span[T]` yield a borrowed `span[T]` view; half-open `[start, stop)` with no copy (§4.6)
|
|
1054
1074
|
- call: `f(x)`
|
|
1055
1075
|
- partial field update: `v.with(x = 10.0)` — returns a copy with specified fields replaced; supported on structs and native types (vector, matrix, quaternion)
|
|
1056
1076
|
- specialization: `name[T]`, `name[32]`, `mod.name[T]`
|
|
@@ -1306,7 +1326,7 @@ Iterator notes for those collection modules:
|
|
|
1306
1326
|
|
|
1307
1327
|
String categories:
|
|
1308
1328
|
|
|
1309
|
-
- `str` -> string view
|
|
1329
|
+
- `str` -> string view. `s[i]` reads the byte at `i` as `ubyte` (bounds-checked); `s[start..stop]` returns a borrowed `str` view using byte offsets (UTF-8 boundary-checked)
|
|
1310
1330
|
- `cstr` -> C ABI string
|
|
1311
1331
|
- `str_buffer[N]` -> fixed-capacity mutable UTF-8 text buffer
|
|
1312
1332
|
|
|
@@ -1373,6 +1393,8 @@ Custom formatting hook notes:
|
|
|
1373
1393
|
- shift operators require integer operands
|
|
1374
1394
|
- safe array indexing requires an addressable array value
|
|
1375
1395
|
- safe indexing (`arr[i]`) is bounds-checked and calls `fatal` on out-of-bounds access
|
|
1396
|
+
- safe range indexing (`a[start..stop]`) is bounds-checked and calls `fatal` on out-of-bounds or out-of-order bounds
|
|
1397
|
+
- `str` range slices require UTF-8 code-unit boundaries at both bounds
|
|
1376
1398
|
- use `get(arr, i)` for recoverable indexing that returns `ptr[T]?` (null on out-of-bounds) instead of aborting
|
|
1377
1399
|
- pointer indexing requires `unsafe`
|
|
1378
1400
|
- `read(...)` of raw pointer requires `unsafe`
|
|
@@ -1622,7 +1644,8 @@ The compiler intentionally rejects the following patterns. These are design cons
|
|
|
1622
1644
|
|
|
1623
1645
|
- `+` concatenates `str`; `cstr` and mixed `str`/`cstr` are not concatenable
|
|
1624
1646
|
- `==`/`!=` on structs and variants requires all fields equality-comparable; use `equal[T]` otherwise (§3.4b)
|
|
1625
|
-
- range expressions are restricted to `for`-loop iterables and range-index assignment targets
|
|
1647
|
+
- range expressions are restricted to `for`-loop iterables, range-index reads (`a[start..stop]`), and range-index assignment targets
|
|
1648
|
+
- `str` views are immutable borrowed text; index and range-index results cannot be assigned through
|
|
1626
1649
|
- functions, methods, generic functions, and variant arms must be called — they are not usable as bare values
|
|
1627
1650
|
- `read(...)` of a raw pointer requires `unsafe`
|
|
1628
1651
|
|
data/lib/milk_tea/base.rb
CHANGED
|
@@ -136,8 +136,9 @@ module MilkTea
|
|
|
136
136
|
return true if @debug_guards
|
|
137
137
|
|
|
138
138
|
collect_checked_array_index_types.any? || collect_checked_span_index_types.any? ||
|
|
139
|
+
!collect_span_slice_helper_names.empty? ||
|
|
139
140
|
uses_format_helpers? ||
|
|
140
|
-
emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_fatal mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_assign mt_str_buffer_append mt_foreign_str_to_cstr_temp mt_foreign_strs_to_cstrs_temp mt_str_concat]) }
|
|
141
|
+
emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_fatal mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_assign mt_str_buffer_append mt_foreign_str_to_cstr_temp mt_foreign_strs_to_cstrs_temp mt_str_concat mt_str_index mt_str_slice]) }
|
|
141
142
|
end
|
|
142
143
|
|
|
143
144
|
def uses_mt_fatal_str_helper?
|
|
@@ -297,6 +298,10 @@ module MilkTea
|
|
|
297
298
|
end
|
|
298
299
|
end
|
|
299
300
|
|
|
301
|
+
def uses_str_slice_helpers?
|
|
302
|
+
emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_index mt_str_slice]) }
|
|
303
|
+
end
|
|
304
|
+
|
|
300
305
|
def uses_variant_equality_helper?
|
|
301
306
|
!variant_equality_types.empty?
|
|
302
307
|
end
|
|
@@ -64,6 +64,36 @@ module MilkTea
|
|
|
64
64
|
]
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
def emit_str_slice_helpers
|
|
68
|
+
[
|
|
69
|
+
"static uint8_t mt_str_index(mt_str text, uintptr_t index) {",
|
|
70
|
+
"#{INDENT}if (index >= text.len) mt_fatal(\"str index out of bounds\");",
|
|
71
|
+
"#{INDENT}return (uint8_t)text.data[index];",
|
|
72
|
+
"}",
|
|
73
|
+
"",
|
|
74
|
+
"static bool mt_str_utf8_boundary(mt_str text, uintptr_t index) {",
|
|
75
|
+
"#{INDENT}if (index == 0 || index == text.len) return true;",
|
|
76
|
+
"#{INDENT}return ((uint8_t)text.data[index] & 0xC0) != 0x80;",
|
|
77
|
+
"}",
|
|
78
|
+
"",
|
|
79
|
+
"static mt_str mt_str_slice(mt_str text, uintptr_t start, uintptr_t stop) {",
|
|
80
|
+
"#{INDENT}if (start > stop || stop > text.len) mt_fatal(\"str slice out of bounds\");",
|
|
81
|
+
"#{INDENT}if (!mt_str_utf8_boundary(text, start) || !mt_str_utf8_boundary(text, stop)) mt_fatal(\"str slice bounds must be UTF-8 code-unit boundaries\");",
|
|
82
|
+
"#{INDENT}return (mt_str){ .data = text.data + start, .len = stop - start };",
|
|
83
|
+
"}",
|
|
84
|
+
]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def emit_span_slice_helper(helper_name)
|
|
88
|
+
span_name = helper_name.sub("mt_span_slice_", "mt_span_")
|
|
89
|
+
[
|
|
90
|
+
"static inline #{span_name} #{helper_name}(#{span_name} span, uintptr_t start, uintptr_t stop) {",
|
|
91
|
+
"#{INDENT}if (start > stop || stop > span.len) mt_fatal(\"span slice out of bounds\");",
|
|
92
|
+
"#{INDENT}return (#{span_name}){ .data = span.data + start, .len = stop - start };",
|
|
93
|
+
"}",
|
|
94
|
+
]
|
|
95
|
+
end
|
|
96
|
+
|
|
67
97
|
def emit_variant_equality_helpers
|
|
68
98
|
variant_decls_by_linkage = (emitted_aggregate_variants + collect_generic_variant_decls).each_with_object({}) { |decl, map| map[decl.linkage_name] = decl }
|
|
69
99
|
variant_equality_types
|
|
@@ -19,6 +19,80 @@ module MilkTea
|
|
|
19
19
|
span_types.uniq
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def collect_span_slice_helper_names
|
|
23
|
+
names = []
|
|
24
|
+
emitted_functions.each do |function|
|
|
25
|
+
collect_span_slice_helper_names_from_statements(function.body, names)
|
|
26
|
+
end
|
|
27
|
+
names.uniq
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def collect_span_slice_helper_names_from_statements(statements, names)
|
|
31
|
+
statements.each do |statement|
|
|
32
|
+
case statement
|
|
33
|
+
when IR::LocalDecl
|
|
34
|
+
collect_span_slice_helper_names_from_expression(statement.value, names)
|
|
35
|
+
when IR::Assignment
|
|
36
|
+
collect_span_slice_helper_names_from_expression(statement.target, names)
|
|
37
|
+
collect_span_slice_helper_names_from_expression(statement.value, names)
|
|
38
|
+
when IR::BlockStmt
|
|
39
|
+
collect_span_slice_helper_names_from_statements(statement.body, names)
|
|
40
|
+
when IR::WhileStmt
|
|
41
|
+
collect_span_slice_helper_names_from_expression(statement.condition, names)
|
|
42
|
+
collect_span_slice_helper_names_from_statements(statement.body, names)
|
|
43
|
+
when IR::ForStmt
|
|
44
|
+
collect_span_slice_helper_names_from_statements([statement.init], names)
|
|
45
|
+
collect_span_slice_helper_names_from_expression(statement.condition, names)
|
|
46
|
+
collect_span_slice_helper_names_from_statements(statement.body, names)
|
|
47
|
+
collect_span_slice_helper_names_from_statements([statement.post], names)
|
|
48
|
+
when IR::IfStmt
|
|
49
|
+
collect_span_slice_helper_names_from_expression(statement.condition, names)
|
|
50
|
+
collect_span_slice_helper_names_from_statements(statement.then_body, names)
|
|
51
|
+
collect_span_slice_helper_names_from_statements(statement.else_body, names) if statement.else_body
|
|
52
|
+
when IR::SwitchStmt
|
|
53
|
+
collect_span_slice_helper_names_from_expression(statement.expression, names)
|
|
54
|
+
statement.cases.each { |switch_case| collect_span_slice_helper_names_from_statements(switch_case.body, names) }
|
|
55
|
+
when IR::ReturnStmt
|
|
56
|
+
collect_span_slice_helper_names_from_expression(statement.value, names) if statement.value
|
|
57
|
+
when IR::ExpressionStmt
|
|
58
|
+
collect_span_slice_helper_names_from_expression(statement.expression, names)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def collect_span_slice_helper_names_from_expression(expression, names)
|
|
64
|
+
return unless expression
|
|
65
|
+
|
|
66
|
+
case expression
|
|
67
|
+
when IR::Call
|
|
68
|
+
names << expression.callee if expression.callee.is_a?(String) && expression.callee.start_with?("mt_span_slice_")
|
|
69
|
+
collect_span_slice_helper_names_from_expression(expression.callee, names) unless expression.callee.is_a?(String)
|
|
70
|
+
expression.arguments.each { |argument| collect_span_slice_helper_names_from_expression(argument, names) }
|
|
71
|
+
when IR::Member
|
|
72
|
+
collect_span_slice_helper_names_from_expression(expression.receiver, names)
|
|
73
|
+
when IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex
|
|
74
|
+
collect_span_slice_helper_names_from_expression(expression.receiver, names)
|
|
75
|
+
collect_span_slice_helper_names_from_expression(expression.index, names)
|
|
76
|
+
when IR::Unary
|
|
77
|
+
collect_span_slice_helper_names_from_expression(expression.operand, names)
|
|
78
|
+
when IR::Binary
|
|
79
|
+
collect_span_slice_helper_names_from_expression(expression.left, names)
|
|
80
|
+
collect_span_slice_helper_names_from_expression(expression.right, names)
|
|
81
|
+
when IR::Conditional
|
|
82
|
+
collect_span_slice_helper_names_from_expression(expression.condition, names)
|
|
83
|
+
collect_span_slice_helper_names_from_expression(expression.then_expression, names)
|
|
84
|
+
collect_span_slice_helper_names_from_expression(expression.else_expression, names)
|
|
85
|
+
when IR::Cast, IR::AddressOf
|
|
86
|
+
collect_span_slice_helper_names_from_expression(expression.expression, names)
|
|
87
|
+
when IR::AggregateLiteral
|
|
88
|
+
expression.fields.each { |field| collect_span_slice_helper_names_from_expression(field.value, names) }
|
|
89
|
+
when IR::ArrayLiteral
|
|
90
|
+
expression.elements.each { |element| collect_span_slice_helper_names_from_expression(element, names) }
|
|
91
|
+
when IR::VariantLiteral
|
|
92
|
+
expression.fields.each { |field| collect_span_slice_helper_names_from_expression(field.value, names) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
22
96
|
def collect_checked_array_index_types_from_statements(statements, array_types, nullable_only: false)
|
|
23
97
|
statements.each do |statement|
|
|
24
98
|
case statement
|
|
@@ -162,6 +162,10 @@ module MilkTea
|
|
|
162
162
|
lines.concat(emit_str_concat_helper)
|
|
163
163
|
lines << ""
|
|
164
164
|
end
|
|
165
|
+
if uses_str_slice_helpers?
|
|
166
|
+
lines.concat(emit_str_slice_helpers)
|
|
167
|
+
lines << ""
|
|
168
|
+
end
|
|
165
169
|
if uses_str_buffer_helpers?
|
|
166
170
|
lines.concat(emit_utf8_validation_helpers)
|
|
167
171
|
lines << ""
|
|
@@ -335,6 +339,11 @@ module MilkTea
|
|
|
335
339
|
lines << ""
|
|
336
340
|
end
|
|
337
341
|
|
|
342
|
+
collect_span_slice_helper_names.each do |helper_name|
|
|
343
|
+
lines.concat(emit_span_slice_helper(helper_name))
|
|
344
|
+
lines << ""
|
|
345
|
+
end
|
|
346
|
+
|
|
338
347
|
collect_checked_array_index_types(nullable_only: true).each do |type|
|
|
339
348
|
lines.concat(emit_nullable_array_index_helper(type))
|
|
340
349
|
lines << ""
|
|
@@ -400,6 +400,8 @@ module MilkTea
|
|
|
400
400
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["span"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.element_type))], nullable: false)
|
|
401
401
|
when Types::Task
|
|
402
402
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["Task"]), arguments: [AST::TypeArgument.new(value: ast_type_ref_for(type.result_type))], nullable: false)
|
|
403
|
+
when Types::StringView
|
|
404
|
+
AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["str"]), arguments: [], nullable: false)
|
|
403
405
|
when Types::TypeVar
|
|
404
406
|
AST::TypeRef.new(name: AST::QualifiedName.new(parts: [type.name]), arguments: [], nullable: false)
|
|
405
407
|
when Types::StructInstance
|
|
@@ -1364,13 +1364,19 @@ module MilkTea
|
|
|
1364
1364
|
when AST::IndexAccess
|
|
1365
1365
|
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
1366
1366
|
receiver = lower_expression(expression.receiver, env:)
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1370
|
-
elsif receiver_type.is_a?(Types::Span)
|
|
1371
|
-
IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1367
|
+
if expression.index.is_a?(AST::RangeExpr)
|
|
1368
|
+
lower_range_index_access(receiver, receiver_type, expression.index, env:, type:)
|
|
1372
1369
|
else
|
|
1373
|
-
|
|
1370
|
+
index = lower_expression(expression.index, env:)
|
|
1371
|
+
if array_type?(receiver_type) && addressable_storage_expression?(expression.receiver)
|
|
1372
|
+
IR::CheckedIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1373
|
+
elsif receiver_type.is_a?(Types::Span)
|
|
1374
|
+
IR::CheckedSpanIndex.new(receiver:, index:, receiver_type:, type:)
|
|
1375
|
+
elsif receiver_type.is_a?(Types::StringView)
|
|
1376
|
+
IR::Call.new(callee: "mt_str_index", arguments: [receiver, index], type:)
|
|
1377
|
+
else
|
|
1378
|
+
IR::Index.new(receiver:, index:, type:)
|
|
1379
|
+
end
|
|
1374
1380
|
end
|
|
1375
1381
|
when AST::UnaryOp
|
|
1376
1382
|
raise LoweringError.new("propagation expressions must be prepared before direct lowering", line: 0, column: 0, path: @ctx.current_analysis_path) if expression.operator == "?"
|
|
@@ -1452,6 +1458,25 @@ module MilkTea
|
|
|
1452
1458
|
end
|
|
1453
1459
|
end
|
|
1454
1460
|
|
|
1461
|
+
def lower_range_index_access(receiver, receiver_type, range, env:, type:)
|
|
1462
|
+
start = lower_range_index_bound(range.start_expr, env:)
|
|
1463
|
+
stop = lower_range_index_bound(range.end_expr, env:)
|
|
1464
|
+
|
|
1465
|
+
if receiver_type.is_a?(Types::StringView)
|
|
1466
|
+
return IR::Call.new(callee: "mt_str_slice", arguments: [receiver, start, stop], type:)
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
span_type = range_index_result_type(receiver_type)
|
|
1470
|
+
span_argument = receiver_type.is_a?(Types::Span) ? receiver : lower_array_to_span_expression(receiver, span_type)
|
|
1471
|
+
IR::Call.new(callee: span_slice_callee(span_type), arguments: [span_argument, start, stop], type: span_type)
|
|
1472
|
+
end
|
|
1473
|
+
|
|
1474
|
+
def lower_range_index_bound(bound, env:)
|
|
1475
|
+
ptr_uint = @ctx.types.fetch("ptr_uint")
|
|
1476
|
+
lowered = lower_expression(bound, env:, expected_type: ptr_uint)
|
|
1477
|
+
lowered.type == ptr_uint ? lowered : IR::Cast.new(target_type: ptr_uint, expression: lowered, type: ptr_uint)
|
|
1478
|
+
end
|
|
1479
|
+
|
|
1455
1480
|
def lower_member_access(expression, env:, type:)
|
|
1456
1481
|
if (type_expr = resolve_type_expression(expression.receiver))
|
|
1457
1482
|
if type_expr.is_a?(Types::Variant)
|
|
@@ -206,6 +206,9 @@ module MilkTea
|
|
|
206
206
|
when AST::IndexAccess
|
|
207
207
|
collect_proc_captures_from_expression(expression.receiver, env, local_scopes, captures)
|
|
208
208
|
collect_proc_captures_from_expression(expression.index, env, local_scopes, captures)
|
|
209
|
+
when AST::RangeExpr
|
|
210
|
+
collect_proc_captures_from_expression(expression.start_expr, env, local_scopes, captures)
|
|
211
|
+
collect_proc_captures_from_expression(expression.end_expr, env, local_scopes, captures)
|
|
209
212
|
when AST::Specialization
|
|
210
213
|
collect_proc_captures_from_expression(expression.callee, env, local_scopes, captures)
|
|
211
214
|
expression.arguments.each { |argument| collect_proc_captures_from_expression(argument.value, env, local_scopes, captures) }
|
|
@@ -786,6 +786,9 @@ module MilkTea
|
|
|
786
786
|
raise LoweringError.new("unknown member #{expression.member}", line: expression.line, column: expression.column, path: @ctx.current_analysis_path)
|
|
787
787
|
when AST::IndexAccess
|
|
788
788
|
receiver_type = infer_expression_type(expression.receiver, env:)
|
|
789
|
+
if expression.index.is_a?(AST::RangeExpr)
|
|
790
|
+
return range_index_result_type(receiver_type)
|
|
791
|
+
end
|
|
789
792
|
index_type = infer_expression_type(expression.index, env:)
|
|
790
793
|
infer_index_result_type(receiver_type, index_type)
|
|
791
794
|
when AST::UnaryOp
|
|
@@ -213,6 +213,10 @@ module MilkTea
|
|
|
213
213
|
|
|
214
214
|
receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
215
215
|
|
|
216
|
+
if receiver_type.is_a?(Types::StringView)
|
|
217
|
+
return @ctx.types.fetch("ubyte")
|
|
218
|
+
end
|
|
219
|
+
|
|
216
220
|
if array_type?(receiver_type)
|
|
217
221
|
return array_element_type(receiver_type)
|
|
218
222
|
end
|
|
@@ -236,6 +240,28 @@ module MilkTea
|
|
|
236
240
|
raise LoweringError.new("cannot index #{receiver_type}", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
237
241
|
end
|
|
238
242
|
|
|
243
|
+
def range_index_result_type(receiver_type)
|
|
244
|
+
receiver_type = referenced_type(receiver_type) if ref_type?(receiver_type)
|
|
245
|
+
|
|
246
|
+
if receiver_type.is_a?(Types::StringView)
|
|
247
|
+
return @ctx.types.fetch("str")
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
if array_type?(receiver_type)
|
|
251
|
+
return Types::Span.new(array_element_type(receiver_type))
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
if receiver_type.is_a?(Types::Span)
|
|
255
|
+
return receiver_type
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
raise LoweringError.new("cannot range-index #{receiver_type}; expected str, array[T, N], or span[T]", line: 0, column: 0, path: @ctx.current_analysis_path)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def span_slice_callee(span_type)
|
|
262
|
+
"mt_span_slice_#{sanitize_identifier(span_type.element_type.to_s)}"
|
|
263
|
+
end
|
|
264
|
+
|
|
239
265
|
def stored_ref_supported_type?(type, visited = {})
|
|
240
266
|
return true unless type
|
|
241
267
|
|
|
@@ -37,6 +37,8 @@ module MilkTea
|
|
|
37
37
|
allow_span_param_identifier: true,
|
|
38
38
|
)
|
|
39
39
|
|
|
40
|
+
raise_sema_error("cannot assign through str index; str is an immutable borrowed view") if receiver_type.is_a?(Types::StringView)
|
|
41
|
+
|
|
40
42
|
index_type = infer_expression(expression.index, scopes:)
|
|
41
43
|
infer_index_result_type(receiver_type, index_type)
|
|
42
44
|
when AST::Call
|
|
@@ -101,6 +103,8 @@ module MilkTea
|
|
|
101
103
|
require_mutable_pointer:,
|
|
102
104
|
allow_span_param_identifier:,
|
|
103
105
|
)
|
|
106
|
+
raise_sema_error("cannot index through str as an assignment receiver; str is an immutable borrowed view") if receiver_type.is_a?(Types::StringView)
|
|
107
|
+
|
|
104
108
|
index_type = infer_expression(expression.index, scopes:)
|
|
105
109
|
infer_index_result_type(receiver_type, index_type)
|
|
106
110
|
when AST::Call
|
|
@@ -470,8 +474,17 @@ module MilkTea
|
|
|
470
474
|
|
|
471
475
|
def infer_index_access(expression, scopes:)
|
|
472
476
|
receiver_type = infer_expression(expression.receiver, scopes:)
|
|
477
|
+
|
|
478
|
+
if expression.index.is_a?(AST::RangeExpr)
|
|
479
|
+
return infer_range_index_access(expression, receiver_type, scopes:)
|
|
480
|
+
end
|
|
481
|
+
|
|
473
482
|
index_type = infer_expression(expression.index, scopes:)
|
|
474
483
|
|
|
484
|
+
if receiver_type.is_a?(Types::StringView) && (literal = integer_literal_bound_value(expression.index)) && literal < 0
|
|
485
|
+
raise_sema_error("str index #{literal} is negative; str indices must be non-negative", expression)
|
|
486
|
+
end
|
|
487
|
+
|
|
475
488
|
if soa_type?(receiver_type)
|
|
476
489
|
return receiver_type.element_type
|
|
477
490
|
end
|
|
@@ -1022,6 +1022,10 @@ module MilkTea
|
|
|
1022
1022
|
def infer_index_result_type(receiver_type, index_type)
|
|
1023
1023
|
raise_sema_error("index must be an integer type, got #{index_type}") unless integer_type?(index_type)
|
|
1024
1024
|
|
|
1025
|
+
if receiver_type.is_a?(Types::StringView)
|
|
1026
|
+
return @ctx.types.fetch("ubyte")
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1025
1029
|
if array_type?(receiver_type)
|
|
1026
1030
|
return array_element_type(receiver_type)
|
|
1027
1031
|
end
|
|
@@ -1047,6 +1051,48 @@ module MilkTea
|
|
|
1047
1051
|
raise_sema_error("cannot index #{receiver_type}")
|
|
1048
1052
|
end
|
|
1049
1053
|
|
|
1054
|
+
def infer_range_index_access(expression, receiver_type, scopes:)
|
|
1055
|
+
start_type = infer_expression(expression.index.start_expr, scopes:)
|
|
1056
|
+
stop_type = infer_expression(expression.index.end_expr, scopes:)
|
|
1057
|
+
raise_sema_error("range index bounds must be integer types, got #{start_type} and #{stop_type}") unless start_type.integer? && stop_type.integer?
|
|
1058
|
+
|
|
1059
|
+
if array_type?(receiver_type) && !addressable_storage_expression?(expression.receiver, scopes:)
|
|
1060
|
+
raise_sema_error("array range slice requires an addressable array value; bind it to a local first", expression)
|
|
1061
|
+
end
|
|
1062
|
+
|
|
1063
|
+
if array_type?(receiver_type) && (start_val = integer_literal_bound_value(expression.index.start_expr)) && (stop_val = integer_literal_bound_value(expression.index.end_expr))
|
|
1064
|
+
length = array_length(receiver_type)
|
|
1065
|
+
unless start_val >= 0 && start_val <= stop_val && stop_val <= length
|
|
1066
|
+
raise_sema_error("range index [#{start_val}..#{stop_val}] is out of bounds for array[T, #{length}]", expression)
|
|
1067
|
+
end
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
range_index_result_type(receiver_type)
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
def range_index_result_type(receiver_type)
|
|
1074
|
+
if receiver_type.is_a?(Types::StringView)
|
|
1075
|
+
return @ctx.types.fetch("str")
|
|
1076
|
+
end
|
|
1077
|
+
|
|
1078
|
+
if array_type?(receiver_type)
|
|
1079
|
+
return Types::Span.new(array_element_type(receiver_type))
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
if span_type?(receiver_type)
|
|
1083
|
+
return receiver_type
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
raise_sema_error("cannot range-index #{receiver_type}; expected str, array[T, N], or span[T]")
|
|
1087
|
+
end
|
|
1088
|
+
|
|
1089
|
+
def integer_literal_bound_value(expression)
|
|
1090
|
+
return expression.value if expression.is_a?(AST::IntegerLiteral)
|
|
1091
|
+
return -expression.operand.value if expression.is_a?(AST::UnaryOp) && expression.operator == "-" && expression.operand.is_a?(AST::IntegerLiteral)
|
|
1092
|
+
|
|
1093
|
+
nil
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1050
1096
|
def addressable_storage_expression?(expression, scopes:)
|
|
1051
1097
|
case expression
|
|
1052
1098
|
when AST::Identifier
|
|
@@ -538,6 +538,7 @@ module MilkTea
|
|
|
538
538
|
require_mutable_pointer: true,
|
|
539
539
|
allow_span_param_identifier: true,
|
|
540
540
|
)
|
|
541
|
+
raise_sema_error("cannot assign through str index; str is an immutable borrowed view") if receiver_type.is_a?(Types::StringView)
|
|
541
542
|
element_type = infer_index_result_type(receiver_type, @ctx.types.fetch("ptr_uint"))
|
|
542
543
|
|
|
543
544
|
statement.value.elements.each_with_index do |elem, i|
|
|
@@ -21,40 +21,63 @@ module MilkTea
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def self.nullable(base)
|
|
24
|
-
_intern([:nullable, base]) { Nullable.new(base) }
|
|
24
|
+
_intern([:nullable, type_signature(base)]) { Nullable.new(base) }
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def self.generic_instance(name, arguments)
|
|
28
28
|
args = arguments.freeze
|
|
29
|
-
_intern([:generic, name, args]) { GenericInstance.new(name, args) }
|
|
29
|
+
_intern([:generic, name, args.map { |a| type_signature(a) }.freeze]) { GenericInstance.new(name, args) }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def self.span(element_type)
|
|
33
|
-
_intern([:span, element_type]) { Span.new(element_type) }
|
|
33
|
+
_intern([:span, type_signature(element_type)]) { Span.new(element_type) }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def self.task(result_type)
|
|
37
|
-
_intern([:task, result_type]) { Task.new(result_type) }
|
|
37
|
+
_intern([:task, type_signature(result_type)]) { Task.new(result_type) }
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def self.string_view
|
|
41
41
|
_intern([:string_view]) { StringView.new }
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
#
|
|
45
|
-
# (assignability must not depend on parameter names), so
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
# never resets the registry between checks)
|
|
44
|
+
# Name-sensitive signature for intern keys. Parameter#eql? is
|
|
45
|
+
# name-insensitive (assignability must not depend on parameter names), so
|
|
46
|
+
# Function/Proc objects (and any wrapper containing them) that differ only
|
|
47
|
+
# in parameter names collide under Hash/==. Pool keys that embed such
|
|
48
|
+
# types raw therefore conflate fn(value: int) with fn(arg0: int) in a
|
|
49
|
+
# long-lived pool (the LSP never resets the registry between checks),
|
|
50
|
+
# leaking one program's parameter names into another's generated C.
|
|
51
|
+
# Embedding names here keeps distinct signatures distinct.
|
|
52
|
+
def self.type_signature(type)
|
|
53
|
+
case type
|
|
54
|
+
when Function
|
|
55
|
+
[:function, type.name, param_signature(type.params), type_signature(type.return_type), type_signature(type.receiver_type), type.receiver_editable, type.variadic, type.external]
|
|
56
|
+
when Proc
|
|
57
|
+
[:proc, param_signature(type.params), type_signature(type.return_type)]
|
|
58
|
+
when Nullable
|
|
59
|
+
[:nullable, type_signature(type.base)]
|
|
60
|
+
when GenericInstance
|
|
61
|
+
[:generic, type.name, type.arguments.map { |a| type_signature(a) }]
|
|
62
|
+
when Tuple
|
|
63
|
+
[:tuple, type.element_types.map { |t| type_signature(t) }, type.field_names]
|
|
64
|
+
when Span
|
|
65
|
+
[:span, type_signature(type.element_type)]
|
|
66
|
+
when Task
|
|
67
|
+
[:task, type_signature(type.result_type)]
|
|
68
|
+
else
|
|
69
|
+
type
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
50
73
|
def self.param_signature(params)
|
|
51
|
-
params.map { |p| [p.name, p.type, p.mutable, p.passing_mode, p.boundary_type] }
|
|
74
|
+
params.map { |p| [p.name, type_signature(p.type), p.mutable, p.passing_mode, p.boundary_type] }
|
|
52
75
|
end
|
|
53
76
|
|
|
54
77
|
def self.function(name, params:, return_type:, receiver_type: nil, receiver_editable: false, variadic: false, external: false)
|
|
55
78
|
params_frozen = params.freeze
|
|
56
79
|
param_key = param_signature(params_frozen)
|
|
57
|
-
_intern([:function, name, param_key, return_type, receiver_type, receiver_editable, variadic, external]) {
|
|
80
|
+
_intern([:function, name, param_key, type_signature(return_type), type_signature(receiver_type), receiver_editable, variadic, external]) {
|
|
58
81
|
Function.new(name, params: params_frozen, return_type: return_type, receiver_type: receiver_type, receiver_editable: receiver_editable, variadic: variadic, external: external)
|
|
59
82
|
}
|
|
60
83
|
end
|
|
@@ -62,7 +85,7 @@ module MilkTea
|
|
|
62
85
|
def self.proc(params:, return_type:)
|
|
63
86
|
params_frozen = params.freeze
|
|
64
87
|
param_key = param_signature(params_frozen)
|
|
65
|
-
_intern([:proc, param_key, return_type]) { Proc.new(params: params_frozen, return_type: return_type) }
|
|
88
|
+
_intern([:proc, param_key, type_signature(return_type)]) { Proc.new(params: params_frozen, return_type: return_type) }
|
|
66
89
|
end
|
|
67
90
|
|
|
68
91
|
def self.parameter(name, type, mutable: false, passing_mode: :plain, boundary_type: nil)
|
|
@@ -74,15 +97,15 @@ module MilkTea
|
|
|
74
97
|
def self.tuple(element_types, field_names: nil)
|
|
75
98
|
et_frozen = element_types.freeze
|
|
76
99
|
fn_frozen = field_names&.freeze
|
|
77
|
-
_intern([:tuple, et_frozen, fn_frozen]) { Tuple.new(et_frozen, field_names: fn_frozen) }
|
|
100
|
+
_intern([:tuple, et_frozen.map { |t| type_signature(t) }.freeze, fn_frozen]) { Tuple.new(et_frozen, field_names: fn_frozen) }
|
|
78
101
|
end
|
|
79
102
|
|
|
80
103
|
def self.soa(element_type, count:)
|
|
81
|
-
_intern([:soa, element_type, count]) { SoA.new(element_type, count: count) }
|
|
104
|
+
_intern([:soa, type_signature(element_type), count]) { SoA.new(element_type, count: count) }
|
|
82
105
|
end
|
|
83
106
|
|
|
84
107
|
def self.simd(element_type, lane_count:)
|
|
85
|
-
_intern([:simd, element_type, lane_count]) { Simd.new(element_type, lane_count: lane_count) }
|
|
108
|
+
_intern([:simd, type_signature(element_type), lane_count]) { Simd.new(element_type, lane_count: lane_count) }
|
|
86
109
|
end
|
|
87
110
|
|
|
88
111
|
def self.lifetime_ref(name)
|
|
@@ -86,7 +86,7 @@ module MilkTea
|
|
|
86
86
|
rescue StandardError
|
|
87
87
|
nil
|
|
88
88
|
end
|
|
89
|
-
clear_shared_module_cache if previous_content != disk_content
|
|
89
|
+
@workspace.clear_shared_module_cache if previous_content != disk_content
|
|
90
90
|
unless defined?(@pull_diagnostics_active) && @pull_diagnostics_active
|
|
91
91
|
@protocol.write_notification('textDocument/publishDiagnostics', {
|
|
92
92
|
uri: uri,
|
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.43
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Long (Teefan) Tran
|
|
@@ -628,7 +628,7 @@ metadata:
|
|
|
628
628
|
homepage_uri: https://teefan.github.io/mt-lang/
|
|
629
629
|
source_code_uri: https://github.com/teefan/mt-lang
|
|
630
630
|
post_install_message: |
|
|
631
|
-
Milk Tea 0.3.
|
|
631
|
+
Milk Tea 0.3.43 installed!
|
|
632
632
|
|
|
633
633
|
System requirements:
|
|
634
634
|
- A C compiler (gcc or clang) must be available on PATH
|