Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bd46d88
スマートポインタ(std::mem::smart — UniquePtr/SharedPtr)をセルフホストライブラリとして追加し、v0.…
shadowlink0122 Aug 14, 2026
41f06a7
v0.17.2の開発を開始: VERSION/ドキュメントバッジ/VSCode拡張のバージョン整合とリリースノート骨子を作成
shadowlink0122 Aug 14, 2026
584062e
v0.17.2: セルフホスティング向け標準ライブラリ整備とコンパイラバグ4件・ライブラリバグ2件の修正
shadowlink0122 Aug 15, 2026
e23d7e4
stdlibの内部ヘルパー・内部メンバへprivate修飾子を付与
shadowlink0122 Aug 15, 2026
48f1650
セルフホスティング補強: 文字列内容ハッシュのStringMap/StringSet・基数フォーマッタ・ヒープソート・Internerを追…
shadowlink0122 Aug 15, 2026
6d8e551
セルフホストパーサのサンプルを追加(examples/08_selfhost_parser・機能別9ファイル分割・#[test]単体テスト付き)
shadowlink0122 Aug 15, 2026
02e106b
セルフホストパーサの階層を細分化(トップはmain.cmのみ・段階ごとのフォルダ構成へ)
shadowlink0122 Aug 15, 2026
0f7978e
セルフホストパーサの品質改善: エラーメッセージ定義の分離・トークン種別のenum化・マジックナンバー排除・テスト拡充
shadowlink0122 Aug 15, 2026
0f9abbe
セルフホストパーサの診断メッセージをi18n対応形式へ変更
shadowlink0122 Aug 15, 2026
54ef8c5
セルフホストパーサの冗長なif連鎖をmatch/switchへ整理
shadowlink0122 Aug 15, 2026
bb28db4
セルフホストパーサのi18nテンプレート表のmatch順をメッセージID→言語へ入れ替え(1つのメッセージの全対訳が1箇所に集まり、ID追…
shadowlink0122 Aug 15, 2026
72b98ba
セルフホストパーサのi18nテンプレート表を対訳配列形式へ簡素化(言語のmatchを廃止し、メッセージIDごとに[英語, 日本語]の配列リ…
shadowlink0122 Aug 15, 2026
f7d56b2
makeターゲットをinstall-vscode-extensionへ改名(操作系は動詞先頭・成果物は名詞の命名規約へ統一。旧名vscod…
shadowlink0122 Aug 15, 2026
410408a
install-vscode-extensionの非推奨エイリアス(旧名vscode-extension-install)を削除
shadowlink0122 Aug 15, 2026
2bbc70f
フォーマッタ: 文レベルメソッドチェーンのTS風折り返しを追加
shadowlink0122 Aug 15, 2026
536a1ba
セルフホストパーサ例: msgf1/2/3をmsgf(MsgId, string[])へ一本化
shadowlink0122 Aug 15, 2026
39dd2ee
セルフホストパーサ例: 診断ビルダー廃止とtests/フォルダ集約
shadowlink0122 Aug 15, 2026
04e2767
native::io::fileとwrite_perfテストのopenフラグをLinux対応に修正
shadowlink0122 Aug 15, 2026
7c7e56b
CI: インタプリタ実行ジョブを統合テストmatrixから削除
shadowlink0122 Aug 15, 2026
08a3570
設計ドキュメント: C++・Rustとの比較(有効な部分とデメリット)を追加
shadowlink0122 Aug 15, 2026
dc91cd1
dtor持ち構造体の暗黙コピー診断を追加(二重解放ハザードの言語側恒久対策)
shadowlink0122 Aug 15, 2026
5dc9eb2
Potential fix for pull request finding
shadowlink0122 Aug 15, 2026
4b9fc57
Potential fix for pull request finding
shadowlink0122 Aug 15, 2026
9cf7812
Potential fix for pull request finding
shadowlink0122 Aug 15, 2026
1a83857
Potential fix for pull request finding
shadowlink0122 Aug 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,7 @@ jobs:
- { id: js-o3, name: "JSコード生成+実行 O3", target: tjp3, backend: js, needs_node: true }
- { id: sv-o0, name: "SV生成テスト O0", target: tsvp0, backend: sv, needs_node: false }
- { id: sv-o3, name: "SV生成テスト O3", target: tsvp3, backend: sv, needs_node: false }
# make test(ローカル既定)に含まれるinterpreterスイートをCIでも検証する
- { id: interp-o3, name: "インタプリタ実行 O3", target: tip3, backend: interpreter, needs_node: false }
# interpreterスイート(MIRインタプリタ)はCIでは実行しない(jit系と検証範囲が重複するためジョブを削減。ローカルのmake testには残置)
runs-on: ${{ matrix.os }}
timeout-minutes: 45

Expand Down
18 changes: 18 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ set(CM_TYPECHECK_SOURCES
src/internal/types/checking/auto_impl.cpp
src/internal/types/checking/utils/compat.cpp
src/internal/types/checking/utils/conversion.cpp
src/internal/types/checking/utils/ownership.cpp
src/internal/types/checking/utils/interp.cpp
src/internal/types/checking/utils/diagnostics.cpp
src/internal/types/checking/utils/naming.cpp
Expand Down Expand Up @@ -845,6 +846,23 @@ if(BUILD_TESTING)
target_link_libraries(narrowing_diag_test gtest_main cm_frontend)
gtest_discover_tests(narrowing_diag_test PROPERTIES LABELS "regression")

# Integration tests - dtor持ち構造体の暗黙コピー診断(二重解放ハザードの言語側対策)
# (Cmプログラムを tests/regression/cases/dtor_copy_diag/ から読み込む)
add_executable(dtor_copy_diag_test
tests/regression/dtor_copy_diag_test.cpp
${CM_LEXER_SOURCES}
${CM_AST_SOURCES}
${CM_PARSER_SOURCES}
${CM_TYPECHECK_SOURCES}
${CM_I18N_SOURCES}
)
set_target_properties(dtor_copy_diag_test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TEST_RUNTIME_OUTPUT_DIRECTORY})
target_compile_definitions(dtor_copy_diag_test PRIVATE
CM_DTOR_COPY_DIAG_CASE_DIR="${CMAKE_SOURCE_DIR}/tests/regression/cases/dtor_copy_diag")
target_include_directories(dtor_copy_diag_test BEFORE PRIVATE ${GTEST_INCLUDE_DIR})
target_link_libraries(dtor_copy_diag_test gtest_main cm_frontend)
gtest_discover_tests(dtor_copy_diag_test PROPERTIES LABELS "regression")

# Integration tests - MIR Lowering
# (Cmプログラムを tests/regression/cases/mir_lowering/ から読み込む)
add_executable(mir_lowering_test
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ vscode-extension:
@echo "✅ VSCode拡張ビルド完了:"
@ls -lh vscode-extension/cm-language-*.vsix | awk '{print " " $$9 " (" $$5 ")"}'

.PHONY: vscode-extension-install
vscode-extension-install: vscode-extension
.PHONY: install-vscode-extension
install-vscode-extension: vscode-extension
@VERSION=$$(cat VERSION | tr -d '[:space:]'); \
CODE_BIN=$$(command -v code || true); \
if [ -z "$$CODE_BIN" ] && [ -x "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" ]; then \
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.17.1
0.17.2
37 changes: 37 additions & 0 deletions docs/archive/v0.17.2/bugfix-aot-struct-dce.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# v0.17.2 バグ修正: ポインタ経由参照のみの構造体がプログラムDCEで削除される(AOT限定の不正IR)

セルフホスティング向け標準ライブラリの実装中に発見した6件目の不具合の記録。
`AtomicSharedPtr`(制御ブロック `AtomicSharedCount` をポインタでのみ保持する)が最初の顕在化点で、v0.17.2で修正済み。

## 症状

`cm run`(JIT)では正常動作するプログラムが、`cm compile`(AOT)だけ「LLVM module verification failed: Load operand must be a pointer」で失敗する。
生成IRを見ると、`&self.count->strong` のような二段ポインタ越しのフィールドアドレス計算から最終フィールドGEPとstoreが**黙って脱落**し、未初期化ローカルの読み出しや `load ptr, i32 1` のような不正命令が残る。

## 真因

コンパイルパイプラインの2段の問題の合成。

1. **プログラムDCEの使用中struct収集がポインタを辿らない**(`mir/passes/cleanup/program_dce.cpp`): 使用中構造体の収集が「値型のローカル」と「構造体フィールドの値型」だけを見ており、`Ctr*` のようにポインタ・型引数経由でしか現れない構造体を「未使用」と誤判定して定義ごと削除していた。
2. **codegenのフィールド投影が型解決失敗を黙って飲む**(`codegen/llvm/core/operand.cpp`): struct定義が消えているためフィールド投影のstruct型解決が失敗し、`getPlaceAddress` がnullptrを返して当該文の命令生成がスキップされる(エラーはデバッグログのみ)。結果として不正IRが検証段まで進む。

プログラムDCEはAOT(`cm compile`)のパイプラインのみで走るため、JITでは顕在化しない。
`SharedPtr` が同じ構造(`SharedCount*`)でも動いていたのは、他の生存判定経路で偶然structが残っていたため。

## 修正

DCEの使用中struct収集に型ノードの再帰走査を導入した(`collect_struct_names_from_type`)。
ポインタ(`element_type`)・配列・型引数(`type_args`)を再帰的に辿り、Struct/Generic種別の名前を全て「使用中」として収集する。
フィールド走査側も同じ再帰ヘルパーで統一し、`Ctr*` フィールドのようなポインタ経由参照も生存扱いになる。

デバッグの決め手は `CM_DUMP_IR=1` によるJIT/AOTのIR直接diff(AOTだけGEP+storeが欠落)と、フィールド投影失敗点への一時診断出力(`structName='Ctr'` の解決失敗を直接確認)。

## 残課題(設計課題)

codegen側の「型解決失敗で命令を黙ってスキップする」挙動は不正IRの温床であり、ハードエラー化(該当関数名・投影位置付きの診断)が望ましい。
v0.17.2ではDCE側の修正でIR不正の実経路は塞がったため、codegen側のエラー化は今後の課題として記録する。

## 回帰テスト

- `tests/common/structs/ptr_only_struct.cm` — ポインタ経由でのみ参照される構造体(ジェネリックimpl内の `Ctr*` フィールド・二段デリファレンスの読み書き)の最小再現(本修正で新規追加)
- `tests/common/stdlib/smart/atomic_shared.cm` — 顕在化点の実利用テスト(llvmスイートでAOT経路を通る)
49 changes: 49 additions & 0 deletions docs/archive/v0.17.2/bugfix-generic-mir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# v0.17.2 バグ修正: ジェネリック特殊化とメソッドレシーバのMIR実体参照

セルフホスティング向け標準ライブラリの実装中に発見した3件のコンパイラバグの記録。
いずれもstdの新モジュール(TreeSet/HashSet/Vector.sortBy等)が最初の実利用者となって顕在化した潜在バグで、v0.17.2で修正済み。

## バグ1: フィールドレシーバのメソッド呼び出しがコピーに対して実行される

**症状**: `w.c.bump()` のように構造体フィールドをレシーバにしたメソッド呼び出しで、メソッド内の `self` フィールド変異が呼び出し元に反映されない(非ジェネリックでも発生)。
HashSet/TreeSetのような「内部にマップを持つラッパー型」が全滅する致命的な問題だった(`self.map.insert()` の件数更新が消え、ヒープ経由の書き込みだけが偶然残る)。

**真因**: MIRのメソッド呼び出しlowering(`expr/call.cpp` のself引数処理)が、レシーバ式の場所化を変数参照(HirVarRef)とポインタデリファレンスの2形だけ特別扱いし、フィールドアクセス(HirMember)等はフォールバックで「一時コピーへの参照」を渡していた。
場所化API `lower_place`(メンバ・インデックス・デリファレンスのチェーンをMirPlaceへ写す唯一の機構)は存在したが、self引数経路から使われていなかった(`resolve_receiver_place` は定義のみのデッドコードだった)。

**修正**: self引数処理へ場所化分岐を追加し、`lower_place` が解決できるレシーバは実体のアドレス(投影付きPlaceへの `MirRvalue::ref`)を渡すようにした。右辺値レシーバ(関数戻り値への直接呼び出し等)のみ従来のコピー渡しへフォールバックする。

## バグ2: ジェネリック関数の関数ポインタ引数でシグネチャが未置換

**症状**: `<T> void f(int*(T, T) cmp, ...)` やジェネリックimplメソッドの `int*(T, T)` 引数経由の間接呼び出しが、LLVM検証エラー「Call parameter type does not match function signature」で失敗する。
`Vector<T>.sortBy(比較ラムダ)` が最初の顕在化点。

**真因**: 型パラメータ置換が2実装とも関数型の内側を辿っていなかった。

- 正準API `ast::substitute_type_params` は element_type / type_args のみ再帰し、`param_types` / `return_type` を辿らず、さらに「リーフ共有」早期リターンの判定にも関数型フィールドが含まれていなかった(未置換のまま共有返却)。
- mono側の `substitute_type_in_type` にはFunction型の分岐自体が無かった。

**修正**: 両実装にFunction型の再帰(param_types / return_type)を追加した。

## バグ3: ジェネリックメソッドが返す `K[]` スライスの要素幅ずれ

**症状**: ジェネリックimplメソッド内で構築した `K[]`(ローカルまたはフィールド)を返すと、呼び出し側の要素読みが交互にゴミ値になる(`[1, ゴミ, 2, ゴミ]`)。メソッド内での読みは正常。
TreeMapの `keys_in_order()`(in-order走査の返却)が最初の顕在化点。

**真因**: ジェネリック関数のMIRはスライス生成の要素サイズを「未解決のK(構造体扱いの既定幅)」で**定数として焼き込む**(`cm_slice_new(elem_size, cap)` の第1引数・`cm_array_to_slice(ptr, len, elem_size)` の第3引数)。
mono特殊化はローカルの型は置換するが、この埋め込み定数と、そもそもターミネータ(呼び出しの引数・戻り値格納先)のplace型を置換対象にしていなかった。
その結果ヘッダの `elem_size` が過大なまま残り、blob規約のpush/getはヘッダ基準で整合する一方、呼び出し側の静的ストライド(int=4)と食い違って読みがずれていた。

**修正**(`mono/specialize.cpp`):

1. ターミネータ(Call引数・destination・SwitchInt判別値)にも型置換を適用する(従来はstatementのみ)。
2. `cm_slice_new` / `cm_array_to_slice` の要素サイズ引数を、置換後の宛先スライス型から `layout::slice_elem_stride_of`(集約サイズは `calculate_specialized_type_size`)で再計算して差し替える。blob系push/getはヘッダの `elem_size` を参照するサイズ非依存の規約のため、ヘッダサイズの補正だけで全経路が整合する。

既存の `sizeof_for_T` マーカー(ジェネリックsizeofの遅延解決)と同じ「ジェネリック依存の定数はmonoで再計算する」方針への追従であり、スライス生成の2関数がこの方針から漏れていた。

## 回帰テスト

- `tests/common/structs/field_receiver.cm`: フィールドレシーバ変異(直接・main経由・非ジェネリックimpl経由・ジェネリックimpl経由)
- `tests/common/generics/functions/fnptr_param.cm`: ジェネリック自由関数・implメソッドの関数ポインタ引数
- `tests/common/generics/slices/method_return.cm`: `K[]` 返却(ローカル構築・フィールド構築・部分修飾)と要素値の完全一致
- TreeMap/TreeSetのin-order走査テスト(stdlib側)も同経路を常時検証する
47 changes: 47 additions & 0 deletions docs/archive/v0.17.2/bugfix-hashset-double-free.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# v0.17.2 バグ修正: HashSetコンストラクタの二重解放(dtor持ち構造体の暗黙コピー)

セルフホスティング向け標準ライブラリの実装中に発見した4件目の不具合の記録。
コンパイラ本体の欠陥ではなく、Cmの現行セマンティクス(構造体代入は浅いコピー・コピー時に所有権の概念なし)とdtor持ち構造体の組み合わせで生じるライブラリ側の二重解放で、v0.17.2でライブラリ側を修正済み。

## 症状

`HashSet<T>` を使うプログラムが**非決定的に**SIGABRT(`pointer being freed was not allocated`)またはSIGTRAPで異常終了する。
同一バイナリ・同一プログラムでも実行ごとに完走・途中クラッシュが揺れ(10回中3回程度失敗)、ヒープの再利用状況に依存するため再現条件が安定しない。
表面上は「大量挿入でハングする」ように見え、当初はパフォーマンス問題やコンパイラのループと誤認しやすい。

## 真因

`HashSet.self()` がローカルで `HashMap<T, bool> m()` を構築し `self.map = m;` で代入していた。
Cmの構造体代入は浅いコピーであり、コンストラクタ終端で `m` のデストラクタが走って `entries` バッファを解放するため、`self.map.entries` は最初からdanglingポインタになる。
以後の挿入は解放済みメモリへの書き込みとして偶然動作し、`grow()` が旧バッファを `dealloc` した時点で二重解放としてmallocに検出される(検出されない実行では完走する)。

デバッグの決め手はハング中プロセスの `sample` によるスタック採取で、JITフレーム内からの `___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED` が直接得られた。

## 修正

ライブラリ側(`libs/std/collections/hashset.cm`)で、代入後にローカル `m` の保持ポインタを明示的に無効化して所有権移動を表現した。
`HashMap` のデストラクタはnullポインタを解放しないため、`m` のdtorは無害化される。

```cm
self() {
HashMap<T, bool> m();
self.map = m;
// mのdtorによる二重解放を防ぐ(所有権はself.mapへ移動済み)
m.entries = 0 as Entry<T, bool>*;
}
```

同型のラッパーである `TreeSet`(`TreeMap` を内包)は、`TreeMap` がスライス(ランタイム管理)ベースでデストラクタを持たないため影響しない。
`libs/std` 全体を「ローカル構築→フィールド代入」パターンで横断確認し、dtor持ち構造体の該当箇所は `HashSet` のみだった。

## 言語側のフォローアップ(v0.17.2で実装済み)

この事故はライブラリ利用者が同じパターン(dtor持ち構造体を値としてコピー・フィールドへ代入)を書けば誰でも踏む。
恒久対策として挙げた「dtor持ち構造体の暗黙コピーへの警告」はv0.17.2内で実装した([設計文書](dtor-copy-diagnostic.html)。let初期化・代入・return・構造体リテラルフィールドで警告、--strictでエラー)。
「move代入(`self.map = move m;`)のサポート」は既存のmove機構で既に機能することを確認し、本記録の回避策(ポインタの手動無効化)は `self.map = move m;` へ置き換えた。

## 回帰テスト

- `tests/common/stdlib/collections/hashset_basic.cm` — 機能検証(既存)
- `tests/common/stdlib/collections/hashset_load.cm` — 10000件の挿入・参照・半数削除のパフォーマンススモーク(本修正で新規追加。修正前は非決定的クラッシュ)
- `tests/benchmarks/cm/09_hashset_ops.cm` — 50000件のベンチマーク
34 changes: 34 additions & 0 deletions docs/archive/v0.17.2/bugfix-js-slice-pointer-arg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# v0.17.2 バグ記録: JSバックエンドのスライスポインタ引数(K[]*)未対応

セルフホスティング向け標準ライブラリの実装中に発見した7件目の不具合の記録。
`TreeMap.keys_in_order()` の内部再帰ヘルパー `collect_keys(int node, K[]* out)` が最初の顕在化点で、v0.17.2ではライブラリ側の書き換えで回避した(バックエンド側は未修正の既知制限として記録)。

## 症状

スライスへのポインタ(`K[]*`)を関数引数として渡し、呼び出し先で `(*out).push(...)` すると、JSバックエンドだけ実行時エラーになる。

```
TypeError: Cannot read properties of undefined (reading 'undefined')
at TreeMap__int__bool__collect_keys (...:776:38)
```

native(JIT/AOT)・interpreterでは正常動作する。

## 真因

JSバックエンドのポインタ表現は `{__arr, __idx}`(配列+添字のセル参照)だが、スライス(JS配列)そのものを指すポインタのデリファレンス+メソッド呼び出し(`__cm_unwrap(out.__arr[out.__idx]).push(...)`)で参照セルの解決が壊れる。
ローカル変数のアドレス渡し一般では動くが、再帰呼び出しへ引き回されるスライスポインタで `__arr` が未定義になる。

## 回避(ライブラリ側)

`TreeMap.keys_in_order()` を再帰ヘルパー+出力ポインタ方式から、明示スタックの反復in-order走査へ書き換えた(`libs/std/collections/treemap.cm` に制約コメントあり)。
挙動・計算量(O(n))は同一で、全バックエンドで同一結果になる。

## 残課題(バックエンド側)

JSバックエンドのスライスポインタ引数の参照セル解決の修正は今後の課題として記録する。
それまで、libsおよびユーザコードでJS対象のコードは「スライスを関数で育てる」場合に出力ポインタ引数でなく戻り値返し・反復走査を使うこと。

## 回帰テスト

- `tests/common/stdlib/collections/treeset_basic.cm` / `treemap_remove.cm` / `treeset_balance.cm` — jsスイートで反復走査版keys_in_orderの動作を担保
Loading
Loading