chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更

Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-21 06:25:17 +09:00
parent baf028a94f
commit f9d100ce01
366 changed files with 14322 additions and 5236 deletions

View File

@ -25,8 +25,8 @@ pub fn populate_from_toml(
for name in candidates.iter() {
let p = std::path::Path::new(name);
if p.exists() {
let txt = std::fs::read_to_string(p)
.map_err(|e| UsingError::ReadToml(e.to_string()))?;
let txt =
std::fs::read_to_string(p).map_err(|e| UsingError::ReadToml(e.to_string()))?;
found = Some((txt, p.to_path_buf()));
break;
}
@ -48,8 +48,8 @@ pub fn populate_from_toml(
// 3) Fallback: empty content and path
Ok(found.unwrap_or((String::new(), std::path::PathBuf::from(""))))
}?;
let doc = toml::from_str::<toml::Value>(&text)
.map_err(|e| UsingError::ParseToml(e.to_string()))?;
let doc =
toml::from_str::<toml::Value>(&text).map_err(|e| UsingError::ParseToml(e.to_string()))?;
let toml_dir = toml_path
.parent()
.map(|p| p.to_path_buf())
@ -59,7 +59,11 @@ pub fn populate_from_toml(
if let Some(mods) = doc.get("modules").and_then(|v| v.as_table()) {
fn visit(prefix: &str, tbl: &toml::value::Table, out: &mut Vec<(String, String)>) {
for (k, v) in tbl.iter() {
let name = if prefix.is_empty() { k.to_string() } else { format!("{}.{}", prefix, k) };
let name = if prefix.is_empty() {
k.to_string()
} else {
format!("{}.{}", prefix, k)
};
if let Some(s) = v.as_str() {
out.push((name, s.to_string()));
} else if let Some(t) = v.as_table() {
@ -97,15 +101,35 @@ pub fn populate_from_toml(
}
// named packages: any subtable not paths/aliases is a package
for (k, v) in using_tbl.iter() {
if k == "paths" || k == "aliases" { continue; }
if k == "paths" || k == "aliases" {
continue;
}
if let Some(tbl) = v.as_table() {
let kind = tbl.get("kind").and_then(|x| x.as_str()).map(PackageKind::from_str).unwrap_or(PackageKind::Package);
let kind = tbl
.get("kind")
.and_then(|x| x.as_str())
.map(PackageKind::from_str)
.unwrap_or(PackageKind::Package);
// path is required
if let Some(path_s) = tbl.get("path").and_then(|x| x.as_str()) {
let path = path_s.to_string();
let main = tbl.get("main").and_then(|x| x.as_str()).map(|s| s.to_string());
let bid = tbl.get("bid").and_then(|x| x.as_str()).map(|s| s.to_string());
packages.insert(k.to_string(), UsingPackage { kind, path, main, bid });
let main = tbl
.get("main")
.and_then(|x| x.as_str())
.map(|s| s.to_string());
let bid = tbl
.get("bid")
.and_then(|x| x.as_str())
.map(|s| s.to_string());
packages.insert(
k.to_string(),
UsingPackage {
kind,
path,
main,
bid,
},
);
}
}
}
@ -139,7 +163,9 @@ pub fn resolve_using_target_common(
) -> Result<String, String> {
// 1) modules mapping
if let Some((_, p)) = modules.iter().find(|(n, _)| n == tgt) {
if verbose { eprintln!("[using/resolve] modules '{}' -> '{}'", tgt, p); }
if verbose {
eprintln!("[using/resolve] modules '{}' -> '{}'", tgt, p);
}
return Ok(p.clone());
}
// 2) named packages
@ -147,28 +173,43 @@ pub fn resolve_using_target_common(
match pkg.kind {
PackageKind::Dylib => {
let out = format!("dylib:{}", pkg.path);
if verbose { eprintln!("[using/resolve] dylib '{}' -> '{}'", tgt, out); }
if verbose {
eprintln!("[using/resolve] dylib '{}' -> '{}'", tgt, out);
}
return Ok(out);
}
PackageKind::Package => {
let base = std::path::Path::new(&pkg.path);
let out = if let Some(m) = &pkg.main {
if matches!(base.extension().and_then(|s| s.to_str()), Some("nyash") | Some("hako")) {
if matches!(
base.extension().and_then(|s| s.to_str()),
Some("nyash") | Some("hako")
) {
pkg.path.clone()
} else {
base.join(m).to_string_lossy().to_string()
}
} else {
if matches!(base.extension().and_then(|s| s.to_str()), Some("nyash") | Some("hako")) {
if matches!(
base.extension().and_then(|s| s.to_str()),
Some("nyash") | Some("hako")
) {
pkg.path.clone()
} else {
let leaf = base.file_name().and_then(|s| s.to_str()).unwrap_or(tgt);
let hako = base.join(format!("{}.hako", leaf));
if hako.exists() { hako.to_string_lossy().to_string() }
else { base.join(format!("{}.hako", leaf)).to_string_lossy().to_string() }
if hako.exists() {
hako.to_string_lossy().to_string()
} else {
base.join(format!("{}.hako", leaf))
.to_string_lossy()
.to_string()
}
}
};
if verbose { eprintln!("[using/resolve] package '{}' -> '{}'", tgt, out); }
if verbose {
eprintln!("[using/resolve] package '{}' -> '{}'", tgt, out);
}
return Ok(out);
}
}
@ -179,26 +220,41 @@ pub fn resolve_using_target_common(
let mut cand: Vec<String> = Vec::new();
if let Some(dir) = context_dir {
let c1 = dir.join(&rel_hako);
if c1.exists() { cand.push(c1.to_string_lossy().to_string()); }
if c1.exists() {
cand.push(c1.to_string_lossy().to_string());
}
let c2 = dir.join(&rel_ny);
if c2.exists() { cand.push(c2.to_string_lossy().to_string()); }
if c2.exists() {
cand.push(c2.to_string_lossy().to_string());
}
}
for base in using_paths {
let p = std::path::Path::new(base);
let c1 = p.join(&rel_hako);
if c1.exists() { cand.push(c1.to_string_lossy().to_string()); }
if c1.exists() {
cand.push(c1.to_string_lossy().to_string());
}
let c2 = p.join(&rel_ny);
if c2.exists() { cand.push(c2.to_string_lossy().to_string()); }
if c2.exists() {
cand.push(c2.to_string_lossy().to_string());
}
}
if cand.is_empty() {
if verbose { eprintln!("[using] unresolved '{}' (searched: rel+paths)", tgt); }
return Err(format!("using: unresolved '{}': searched relative and using.paths", tgt));
if verbose {
eprintln!("[using] unresolved '{}' (searched: rel+paths)", tgt);
}
return Err(format!(
"using: unresolved '{}': searched relative and using.paths",
tgt
));
}
if cand.len() > 1 && strict {
return Err(format!("ambiguous using '{}': {}", tgt, cand.join(", ")));
}
let out = cand.remove(0);
if verbose { eprintln!("[using/resolve] '{}' -> '{}'", tgt, out); }
if verbose {
eprintln!("[using/resolve] '{}' -> '{}'", tgt, out);
}
Ok(out)
}
@ -211,12 +267,20 @@ fn load_workspace_modules(
let members = workspace_tbl
.get("members")
.and_then(|v| v.as_array())
.ok_or_else(|| UsingError::ParseWorkspaceModule("modules.workspace".into(), "expected members array".into()))?;
.ok_or_else(|| {
UsingError::ParseWorkspaceModule(
"modules.workspace".into(),
"expected members array".into(),
)
})?;
for entry in members {
let raw_path = entry
.as_str()
.ok_or_else(|| UsingError::ParseWorkspaceModule("modules.workspace".into(), "members must be string paths".into()))?;
let raw_path = entry.as_str().ok_or_else(|| {
UsingError::ParseWorkspaceModule(
"modules.workspace".into(),
"members must be string paths".into(),
)
})?;
let module_path = if std::path::Path::new(raw_path).is_absolute() {
std::path::PathBuf::from(raw_path)
} else {
@ -227,10 +291,16 @@ fn load_workspace_modules(
.map(|p| p.to_path_buf())
.unwrap_or_else(|| nyash_dir.to_path_buf());
let module_text = std::fs::read_to_string(&module_path).map_err(|e| {
UsingError::ReadWorkspaceModule(module_path.to_string_lossy().to_string(), e.to_string())
UsingError::ReadWorkspaceModule(
module_path.to_string_lossy().to_string(),
e.to_string(),
)
})?;
let module_doc = toml::from_str::<toml::Value>(&module_text).map_err(|e| {
UsingError::ParseWorkspaceModule(module_path.to_string_lossy().to_string(), e.to_string())
UsingError::ParseWorkspaceModule(
module_path.to_string_lossy().to_string(),
e.to_string(),
)
})?;
let module_name = module_doc
.get("module")