diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 83001214..45f64a12 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -43,6 +43,9 @@ Clone() Reduction — Phase 1 (Arc/str) - `TypeBox.type_parameters: Vec` → `Vec>`. - Adjusted builder/registry and `full_name()` accordingly; public behavior unchanged. - Rationale: share-on-clone for frequently copied identifiers, reduce String allocations. + - TypeRegistry + - `types: HashMap>` → `HashMap, Arc>`(内部型名の共有化)。 + - API互換(`get_type(&str)` は Borrow によりそのまま利用可能)。 Refactor Plan (next 1–2 weeks) 1) Split parse_box_declaration (667 lines) in src/parser/declarations/box_definition.rs diff --git a/src/parser/declarations/box_definition.rs b/src/parser/declarations/box_definition.rs index a2a50ca8..02e90a29 100644 --- a/src/parser/declarations/box_definition.rs +++ b/src/parser/declarations/box_definition.rs @@ -13,6 +13,78 @@ use crate::tokenizer::TokenType; use std::collections::HashMap; impl NyashParser { + /// Validate that birth_once properties do not have cyclic dependencies via me. references + fn validate_birth_once_cycles( + &mut self, + methods: &HashMap, + ) -> Result<(), ParseError> { + if !crate::config::env::unified_members() { + return Ok(()); + } + use std::collections::{HashMap, HashSet}; + // Collect birth_once compute bodies + let mut birth_bodies: HashMap> = HashMap::new(); + for (mname, mast) in methods { + if let Some(prop) = mname.strip_prefix("__compute_birth_") { + if let ASTNode::FunctionDeclaration { body, .. } = mast { + birth_bodies.insert(prop.to_string(), body.clone()); + } + } + } + if birth_bodies.is_empty() { + return Ok(()); + } + // Build dependency graph: A -> {B | me.B used inside A} + let mut deps: HashMap> = HashMap::new(); + let props: HashSet = birth_bodies.keys().cloned().collect(); + for (p, body) in &birth_bodies { + let used = self.ast_collect_me_fields(body); + let mut set = HashSet::new(); + for u in used { + if props.contains(&u) && u != *p { + set.insert(u); + } + } + deps.insert(p.clone(), set); + } + // Detect cycle via DFS + fn has_cycle( + node: &str, + deps: &HashMap>, + temp: &mut HashSet, + perm: &mut HashSet, + ) -> bool { + if perm.contains(node) { + return false; + } + if !temp.insert(node.to_string()) { + return true; // back-edge + } + if let Some(ns) = deps.get(node) { + for n in ns { + if has_cycle(n, deps, temp, perm) { + return true; + } + } + } + temp.remove(node); + perm.insert(node.to_string()); + false + } + let mut perm = HashSet::new(); + let mut temp = HashSet::new(); + for p in deps.keys() { + if has_cycle(p, &deps, &mut temp, &mut perm) { + let line = self.current_token().line; + return Err(ParseError::UnexpectedToken { + found: self.current_token().token_type.clone(), + expected: "birth_once declarations must not have cyclic dependencies".to_string(), + line, + }); + } + } + Ok(()) + } /// Extracted: parse block-first unified member: `{ body } as [once|birth_once]? name : Type [postfix]` /// Returns true if a member was parsed and emitted into `methods`. fn parse_unified_member_block_first( @@ -565,67 +637,7 @@ impl NyashParser { } // birth_once 相互依存の簡易検出(宣言間の循環) - if crate::config::env::unified_members() { - // Collect birth_once compute bodies - use std::collections::{HashMap, HashSet}; - let mut birth_bodies: HashMap> = HashMap::new(); - for (mname, mast) in &methods { - if let Some(prop) = mname.strip_prefix("__compute_birth_") { - if let ASTNode::FunctionDeclaration { body, .. } = mast { - birth_bodies.insert(prop.to_string(), body.clone()); - } - } - } - // Build dependency graph: A -> {B | me.B used inside A} - let mut deps: HashMap> = HashMap::new(); - let props: HashSet = birth_bodies.keys().cloned().collect(); - for (p, body) in &birth_bodies { - let used = self.ast_collect_me_fields(body); - let mut set = HashSet::new(); - for u in used { - if props.contains(&u) && u != *p { - set.insert(u); - } - } - deps.insert(p.clone(), set); - } - // Detect cycle via DFS - fn has_cycle( - node: &str, - deps: &HashMap>, - temp: &mut HashSet, - perm: &mut HashSet, - ) -> bool { - if perm.contains(node) { - return false; - } - if !temp.insert(node.to_string()) { - return true; // back-edge - } - if let Some(ns) = deps.get(node) { - for n in ns { - if has_cycle(n, deps, temp, perm) { - return true; - } - } - } - temp.remove(node); - perm.insert(node.to_string()); - false - } - let mut perm = HashSet::new(); - let mut temp = HashSet::new(); - for p in deps.keys() { - if has_cycle(p, &deps, &mut temp, &mut perm) { - let line = self.current_token().line; - return Err(ParseError::UnexpectedToken { - found: self.current_token().token_type.clone(), - expected: "birth_once declarations must not have cyclic dependencies".to_string(), - line, - }); - } - } - } + self.validate_birth_once_cycles(&methods)?; Ok(ASTNode::BoxDeclaration { name, diff --git a/src/type_box.rs b/src/type_box.rs index bd765340..3282e1f0 100644 --- a/src/type_box.rs +++ b/src/type_box.rs @@ -14,7 +14,7 @@ use std::sync::Arc; /// メソッドシグニチャ情報 #[derive(Debug, Clone)] pub struct MethodSignature { - pub name: String, + pub name: Arc, pub parameters: Vec, pub parameter_types: Vec>, pub return_type: Arc, @@ -22,9 +22,9 @@ pub struct MethodSignature { } impl MethodSignature { - pub fn new(name: String, parameters: Vec) -> Self { + pub fn new>>(name: N, parameters: Vec) -> Self { Self { - name, + name: name.into(), parameters, parameter_types: Vec::new(), return_type: Arc::new(TypeBox::void_type()), @@ -32,14 +32,14 @@ impl MethodSignature { } } - pub fn with_types( - name: String, + pub fn with_types>>( + name: N, parameters: Vec, parameter_types: Vec>, return_type: Arc, ) -> Self { Self { - name, + name: name.into(), parameters, parameter_types, return_type, @@ -58,7 +58,7 @@ pub struct TypeBox { pub fields: HashMap>, /// メソッドシグニチャ情報 - pub methods: HashMap, + pub methods: HashMap, MethodSignature>, /// 親型(継承) pub parent_type: Option>, @@ -295,7 +295,7 @@ impl Display for TypeBox { #[derive(Debug)] pub struct TypeRegistry { /// 登録済み型 - types: HashMap>, + types: HashMap, Arc>, /// 継承チェーン情報(高速化用) inheritance_chains: HashMap>, @@ -342,7 +342,7 @@ impl TypeRegistry { } self.inheritance_chains.insert(name_s.clone(), chain); - self.types.insert(name_s, type_box); + self.types.insert(type_box.name.clone(), type_box); } /// 型を取得 @@ -365,7 +365,7 @@ impl TypeRegistry { /// すべての型名を取得 pub fn get_all_type_names(&self) -> Vec { - self.types.keys().cloned().collect() + self.types.keys().map(|k| k.as_ref().to_string()).collect() } /// ジェネリクス型をインスタンス化 diff --git a/tools/test/smoke/llvm/ir_phi_empty_check_all.sh b/tools/test/smoke/llvm/ir_phi_empty_check_all.sh new file mode 100644 index 00000000..eaad9e45 --- /dev/null +++ b/tools/test/smoke/llvm/ir_phi_empty_check_all.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Run empty-PHI checker across a curated set of test cases +CASES=( + apps/tests/hello_simple_llvm.nyash + apps/tests/loop_if_phi.nyash + apps/tests/llvm_phi_mix.nyash + apps/tests/llvm_phi_heavy_mix.nyash + apps/tests/llvm_phi_try_mix.nyash +) + +DIR="tools/test/smoke/llvm" + +for c in "${CASES[@]}"; do + echo "[phi-empty-check-all] -> $c" + bash "$DIR/ir_phi_empty_check.sh" "$c" +done + +echo "[phi-empty-check-all] OK: all cases passed"