Skip to content

Commit b512ce6

Browse files
committed
Merge upstream/main into perf/stringbuilder-reset
2 parents 95d448f + 4f06d47 commit b512ce6

253 files changed

Lines changed: 5066 additions & 1165 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,80 @@ Each package has its files and blackbox test files (common, ending in `_test.mbt
3232
like `assert_eq` when you are in some loops where each snapshot may vary. You can use `moon coverage analyze > uncovered.log` to see which parts of your code are not covered by tests.
3333

3434
- agent-todo.md has some small tasks that are easy for AI to pick up, agent is welcome to finish the tasks and check the box when you are done
35+
36+
# Trait-method promotion (`extend`)
37+
38+
When a type `impl`s a trait, the trait's methods can be *promoted* to inherent
39+
methods (`x.method(..)`) via an explicit `pub extend` block in the package's
40+
`extends.mbt`. The rule for what we promote vs. deprecate
41+
(`#deprecated(.., skip_current_package=true)` + `#doc(hidden)`) is driven by
42+
**where the trait lives** — because promoting a method couples the type's
43+
package to the trait's package:
44+
45+
- **Traits whose home is `builtin`** (`Add``Sub`, `Compare`, `Eq`, `Hash`,
46+
`Show`, `Default`, `Logger`, `ToStringView`): every package already depends on
47+
`builtin`, so promoting them creates **no cycle** — safe on any type.
48+
- **Traits whose home is a higher package** (`@json.FromJson`,
49+
`@quickcheck.Arbitrary`, `@debug.Debug`): promoting them on a lower-level type
50+
would form a cycle (`builtin → json` etc.), so they are **never promoted**
51+
always use the free function (`@json.from_json`, `@quickcheck.…`,
52+
`@debug.Debug`).
53+
54+
This is about the trait's *logical* home, not the file it currently sits in:
55+
**`ToJson` lives in `builtin` today only for bootstrapping but belongs in
56+
`@json`**, so it is treated as a higher-package trait (not promoted) — promoting
57+
it would have to be ripped out the day it moves.
58+
59+
Within the "safe to promote" set:
60+
61+
- **Operators** (`add`, `sub`, `mul`, `div`, `mod`, `neg`, `land`, `lor`, `lxor`,
62+
`shl`, `shr`): promoted on every type — the named method is a first-class value
63+
the operator syntax can't give you.
64+
- **`Compare::compare`, `Eq::equal`, `Hash::hash`**: promoted **uniformly on
65+
every type** that impls them (they're all in `builtin`, no cycle, and it keeps
66+
the surface uniform). Exactly **one** method per trait is promoted — the
67+
user-facing primary one. Its *derivatives* stay deprecated:
68+
- `Compare::{op_lt, op_le, op_ge, op_gt}` → use `<` `<=` `>=` `>`
69+
- `Eq::not_equal` → use `!=`
70+
- `Hash::hash_combine` → implementer-facing, use it via the trait
71+
72+
(`compare` and `equal` are still worth promoting even though `<` and `==`
73+
exist, because they're the *required* trait methods and the only way to pass
74+
the operation as a first-class value — e.g. `Int::equal` into a fold. The
75+
derivatives above are defaulted members that add nothing over the operator.)
76+
77+
**Tuples** get the same promotions but with `#doc(hidden)` (the synthetic
78+
`Tuple(N)::…` names are ugly in the interface; the methods still work without
79+
a warning).
80+
- **`Show::to_string`**: promoted only for types with a **canonical string
81+
representation** (scalars, strings). Collections deprecate it toward
82+
`@debug.Debug` (there's no single canonical `Show` for a collection).
83+
`Show::output` is never promoted (use `to_string` / `"\{x}"`).
84+
- **`Default::default`**: **deprecated** — a literal (`0`, `[]`, `None`, `""`,
85+
`b'\x00'`) or `Default::default()` via the trait bound is clearer, and it keeps
86+
the interface lean. (Exception: `Byte::default()` is kept promoted **for now**
87+
Moon's generated test drivers call `FixedArray::make(512, Byte::default())`, so
88+
deprecating it breaks `moon test --build-only --deny-warn`. moonbitlang/moon#1938
89+
removes that call; deprecate `Byte` like the rest once a moon release ships.)
90+
- **`ToJson::to_json`**: **not promoted** — use **`Json(x)`**, the constructor of
91+
`Json` (re-exported by the prelude, so it needs no import and works from every
92+
package, `builtin` included). Between that and `Json`'s literal sugar
93+
(`([x, y] : Json)`, `({ "k": v } : Json)`) you rarely need `x.to_json()` at
94+
all, and serialization is cold, so nothing is lost by going through the
95+
constructor. Because `Json(x)` is spelled the same regardless of which package
96+
`Json` lives in, moving `Json`/`ToJson` to `@json` later would not churn call
97+
sites — exactly how `to_repr(x)` is spelled the same though `Debug`/`Repr` live
98+
in `@debug`.
99+
100+
This policy governs a package's own `extends.mbt` (its public API surface).
101+
102+
**Test fixtures should be `priv`.** A type declared in a `_test.mbt` file with no
103+
visibility modifier is *abstract-public*, so `implicit_impl_as_method` fires for
104+
its derived impls and you end up writing `pub extend` ceremony that has nothing
105+
to do with the test. Declare such types `priv` instead — then they need no
106+
`extend` at all. If a test needs to call a promoted method on one, use the trait
107+
form (`ToJson::to_json(x)`, `Default::default()`).
108+
109+
After editing `extends.mbt`, run `moon info && moon fmt` and check the `.mbti`
110+
diff. `#doc(hidden)` alone (no `#deprecated`) keeps a promotion working but hides
111+
it from the generated interface — that's how tuples stay usable but tidy.

NOTICE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,26 @@ Developed at SunPro, a Sun Microsystems, Inc. business.
116116
Permission to use, copy, modify, and distribute this
117117
software is freely granted, provided that this notice
118118
is preserved.
119+
120+
Copyright (c) 1992-2026 The FreeBSD Project.
121+
122+
Redistribution and use in source and binary forms, with or without
123+
modification, are permitted provided that the following conditions
124+
are met:
125+
1. Redistributions of source code must retain the above copyright
126+
notice, this list of conditions and the following disclaimer.
127+
2. Redistributions in binary form must reproduce the above copyright
128+
notice, this list of conditions and the following disclaimer in the
129+
documentation and/or other materials provided with the distribution.
130+
131+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
132+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
133+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
134+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
135+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
136+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
137+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
138+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
139+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
140+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
141+
SUCH DAMAGE.

argparse/extends.mbt

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// Copyright 2026 International Digital Economy Academy
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// --- promoted: kept as regular methods ---
16+
17+
///|
18+
pub extend FlagAction with Eq::{equal}
19+
20+
///|
21+
pub extend FlagAction with Show::{to_string}
22+
23+
///|
24+
pub extend OptionAction with Eq::{equal}
25+
26+
///|
27+
pub extend OptionAction with Show::{to_string}
28+
29+
///|
30+
pub extend ValueRange with Eq::{equal}
31+
32+
///|
33+
pub extend ValueRange with Show::{to_string}
34+
35+
///|
36+
pub extend ValueSource with Eq::{equal}
37+
38+
///|
39+
pub extend ValueSource with Show::{to_string}
40+
41+
// --- deprecated: hidden from the generated interface ---
42+
43+
///|
44+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
45+
#doc(hidden)
46+
pub extend ArgGroup with @debug.Debug::{to_repr}
47+
48+
///|
49+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
50+
#doc(hidden)
51+
pub extend Command with @debug.Debug::{to_repr}
52+
53+
///|
54+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
55+
#doc(hidden)
56+
pub extend FlagAction with @debug.Debug::{to_repr}
57+
58+
///|
59+
#deprecated("Use `!=` instead", skip_current_package=true)
60+
#doc(hidden)
61+
pub extend FlagAction with Eq::{not_equal}
62+
63+
///|
64+
#deprecated("Use `Show::output` via the trait or `to_string` instead", skip_current_package=true)
65+
#doc(hidden)
66+
pub extend FlagAction with Show::{output}
67+
68+
///|
69+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
70+
#doc(hidden)
71+
pub extend FlagArg with @debug.Debug::{to_repr}
72+
73+
///|
74+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
75+
#doc(hidden)
76+
pub extend Matches with @debug.Debug::{to_repr}
77+
78+
///|
79+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
80+
#doc(hidden)
81+
pub extend OptionAction with @debug.Debug::{to_repr}
82+
83+
///|
84+
#deprecated("Use `!=` instead", skip_current_package=true)
85+
#doc(hidden)
86+
pub extend OptionAction with Eq::{not_equal}
87+
88+
///|
89+
#deprecated("Use `Show::output` via the trait or `to_string` instead", skip_current_package=true)
90+
#doc(hidden)
91+
pub extend OptionAction with Show::{output}
92+
93+
///|
94+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
95+
#doc(hidden)
96+
pub extend OptionArg with @debug.Debug::{to_repr}
97+
98+
///|
99+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
100+
#doc(hidden)
101+
pub extend PositionArg with @debug.Debug::{to_repr}
102+
103+
///|
104+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
105+
#doc(hidden)
106+
pub extend ValueRange with @debug.Debug::{to_repr}
107+
108+
///|
109+
#deprecated("Use `!=` instead", skip_current_package=true)
110+
#doc(hidden)
111+
pub extend ValueRange with Eq::{not_equal}
112+
113+
///|
114+
#deprecated("Use `Show::output` via the trait or `to_string` instead", skip_current_package=true)
115+
#doc(hidden)
116+
pub extend ValueRange with Show::{output}
117+
118+
///|
119+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
120+
#doc(hidden)
121+
pub extend ValueSource with @debug.Debug::{to_repr}
122+
123+
///|
124+
#deprecated("Use `!=` instead", skip_current_package=true)
125+
#doc(hidden)
126+
pub extend ValueSource with Eq::{not_equal}
127+
128+
///|
129+
#deprecated("Use `Show::output` via the trait or `to_string` instead", skip_current_package=true)
130+
#doc(hidden)
131+
pub extend ValueSource with Show::{output}

argparse/pkg.generated.mbti

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub(all) enum FlagAction {
3232
Help
3333
Version
3434
} derive(Eq, Show, @debug.Debug)
35+
pub fn FlagAction::equal(Self, Self) -> Bool
36+
pub fn FlagAction::to_string(Self) -> String
3537

3638
pub struct FlagArg {
3739
// private fields
@@ -52,6 +54,8 @@ pub(all) enum OptionAction {
5254
Set
5355
Append
5456
} derive(Eq, Show, @debug.Debug)
57+
pub fn OptionAction::equal(Self, Self) -> Bool
58+
pub fn OptionAction::to_string(Self) -> String
5559

5660
pub struct OptionArg {
5761
// private fields
@@ -70,13 +74,17 @@ pub struct ValueRange {
7074
} derive(Eq, Show, @debug.Debug)
7175
#alias(new, deprecated)
7276
pub fn ValueRange::ValueRange(lower? : Int, upper? : Int) -> Self
77+
pub fn ValueRange::equal(Self, Self) -> Bool
7378
pub fn ValueRange::single() -> Self
79+
pub fn ValueRange::to_string(Self) -> String
7480

7581
pub enum ValueSource {
7682
Argv
7783
Env
7884
Default
7985
} derive(Eq, Show, @debug.Debug)
86+
pub fn ValueSource::equal(Self, Self) -> Bool
87+
pub fn ValueSource::to_string(Self) -> String
8088

8189
// Type aliases
8290

bench/README.mbt.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ test "basic benchmarking" {
1919
})
2020
2121
// The benchmark ran successfully (we can't inspect exact timing)
22-
inspect(summary.to_json().stringify().length() > 0, content="true")
22+
inspect(@json.to_json(summary).stringify().length() > 0, content="true")
2323
}
2424
```
2525

bench/debug.mbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
pub impl @debug.Debug for Bench with fn to_repr(self) {
1717
@debug.Repr::opaque_(
1818
"Bench",
19-
@debug.Repr::array(self.summaries.map(s => @debug.to_repr(s))),
19+
@debug.Repr::array(self.summaries.map(s => Repr(s))),
2020
)
2121
}
2222

bench/extends.mbt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2026 International Digital Economy Academy
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Bench, Summary and Timestamp have no promoted trait methods; every promotion below is deprecated.
16+
17+
// --- deprecated: hidden from the generated interface ---
18+
19+
///|
20+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
21+
#doc(hidden)
22+
pub extend Bench with @debug.Debug::{to_repr}
23+
24+
///|
25+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
26+
#doc(hidden)
27+
pub extend Summary with @debug.Debug::{to_repr}
28+
29+
///|
30+
#deprecated("Use `@json.to_json` instead", skip_current_package=true)
31+
#doc(hidden)
32+
pub extend Summary with ToJson::{to_json}
33+
34+
///|
35+
#deprecated("Use `Debug::to_repr` instead", skip_current_package=true)
36+
#doc(hidden)
37+
pub extend Timestamp with @debug.Debug::{to_repr}

bigint/bigint_js.mbt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,21 +283,21 @@ pub fn BigInt::to_octets(self : BigInt, length? : Int) -> Bytes {
283283
}
284284

285285
///|
286-
extern "js" fn BigInt::compare(self : BigInt, other : BigInt) -> Int =
286+
extern "js" fn BigInt::compare_js(self : BigInt, other : BigInt) -> Int =
287287
#|(x, y) => x < y ? -1 : x > y ? 1 : 0
288288

289289
///|
290290
pub impl Compare for BigInt with fn compare(self, other) {
291-
self.compare(other)
291+
self.compare_js(other)
292292
}
293293

294294
///|
295-
extern "js" fn BigInt::equal(self : BigInt, other : BigInt) -> Bool =
295+
extern "js" fn BigInt::equal_js(self : BigInt, other : BigInt) -> Bool =
296296
#|(x, y) => x === y
297297

298298
///|
299299
pub impl Eq for BigInt with fn equal(self, other) {
300-
self.equal(other)
300+
self.equal_js(other)
301301
}
302302

303303
///|

bigint/bigint_nonjs.mbt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ priv enum Sign {
4747
test "internal repr" {
4848
fn convert(b : BigInt) -> @debug.Repr {
4949
@debug.Repr::record({
50-
"limbs": @debug.to_repr(b.limbs),
51-
"sign": @debug.to_repr(b.sign),
52-
"len": @debug.to_repr(b.len),
50+
"limbs": Repr(b.limbs),
51+
"sign": Repr(b.sign),
52+
"len": Repr(b.len),
5353
})
5454
}
5555
@debug.debug_inspect(
@@ -1527,7 +1527,7 @@ pub fn BigInt::to_octets(self : BigInt, length? : Int) -> Bytes {
15271527
let tail_len = self.len - 1
15281528
let len = (head_bits + 7) / 8 + tail_len * (RADIX_BIT_LEN / 8)
15291529
let len = max(length, len)
1530-
let result = FixedArray::make(len, Byte::default())
1530+
let result = FixedArray::make(len, b'\x00')
15311531
for i in 0..<len {
15321532
if i / 4 >= self.len {
15331533
break

0 commit comments

Comments
 (0)