perf(typebox): reduce clones with Arc<str> for MethodSignature.name and TypeBox.methods keys; API kept stable. tools: add all-cases empty-PHI smoke runner. parser(box): extract birth_once cycle validation into helper

This commit is contained in:
Selfhosting Dev
2025-09-19 12:13:43 +09:00
parent ef1f5206da
commit 5bb7f67c01
4 changed files with 106 additions and 71 deletions

View File

@ -43,6 +43,9 @@ Clone() Reduction — Phase 1 (Arc/str)
- `TypeBox.type_parameters: Vec<String>``Vec<Arc<str>>`. - `TypeBox.type_parameters: Vec<String>``Vec<Arc<str>>`.
- Adjusted builder/registry and `full_name()` accordingly; public behavior unchanged. - Adjusted builder/registry and `full_name()` accordingly; public behavior unchanged.
- Rationale: share-on-clone for frequently copied identifiers, reduce String allocations. - Rationale: share-on-clone for frequently copied identifiers, reduce String allocations.
- TypeRegistry
- `types: HashMap<String, Arc<TypeBox>>``HashMap<Arc<str>, Arc<TypeBox>>`(内部型名の共有化)。
- API互換`get_type(&str)` は Borrow によりそのまま利用可能)。
Refactor Plan (next 12 weeks) Refactor Plan (next 12 weeks)
1) Split parse_box_declaration (667 lines) in src/parser/declarations/box_definition.rs 1) Split parse_box_declaration (667 lines) in src/parser/declarations/box_definition.rs

View File

@ -13,6 +13,78 @@ use crate::tokenizer::TokenType;
use std::collections::HashMap; use std::collections::HashMap;
impl NyashParser { impl NyashParser {
/// Validate that birth_once properties do not have cyclic dependencies via me.<prop> references
fn validate_birth_once_cycles(
&mut self,
methods: &HashMap<String, ASTNode>,
) -> 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<String, Vec<ASTNode>> = 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<String, HashSet<String>> = HashMap::new();
let props: HashSet<String> = 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<String, HashSet<String>>,
temp: &mut HashSet<String>,
perm: &mut HashSet<String>,
) -> 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]` /// 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`. /// Returns true if a member was parsed and emitted into `methods`.
fn parse_unified_member_block_first( fn parse_unified_member_block_first(
@ -565,67 +637,7 @@ impl NyashParser {
} }
// birth_once 相互依存の簡易検出(宣言間の循環) // birth_once 相互依存の簡易検出(宣言間の循環)
if crate::config::env::unified_members() { self.validate_birth_once_cycles(&methods)?;
// Collect birth_once compute bodies
use std::collections::{HashMap, HashSet};
let mut birth_bodies: HashMap<String, Vec<ASTNode>> = 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<String, HashSet<String>> = HashMap::new();
let props: HashSet<String> = 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<String, HashSet<String>>,
temp: &mut HashSet<String>,
perm: &mut HashSet<String>,
) -> 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(ASTNode::BoxDeclaration { Ok(ASTNode::BoxDeclaration {
name, name,

View File

@ -14,7 +14,7 @@ use std::sync::Arc;
/// メソッドシグニチャ情報 /// メソッドシグニチャ情報
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MethodSignature { pub struct MethodSignature {
pub name: String, pub name: Arc<str>,
pub parameters: Vec<String>, pub parameters: Vec<String>,
pub parameter_types: Vec<Arc<TypeBox>>, pub parameter_types: Vec<Arc<TypeBox>>,
pub return_type: Arc<TypeBox>, pub return_type: Arc<TypeBox>,
@ -22,9 +22,9 @@ pub struct MethodSignature {
} }
impl MethodSignature { impl MethodSignature {
pub fn new(name: String, parameters: Vec<String>) -> Self { pub fn new<N: Into<Arc<str>>>(name: N, parameters: Vec<String>) -> Self {
Self { Self {
name, name: name.into(),
parameters, parameters,
parameter_types: Vec::new(), parameter_types: Vec::new(),
return_type: Arc::new(TypeBox::void_type()), return_type: Arc::new(TypeBox::void_type()),
@ -32,14 +32,14 @@ impl MethodSignature {
} }
} }
pub fn with_types( pub fn with_types<N: Into<Arc<str>>>(
name: String, name: N,
parameters: Vec<String>, parameters: Vec<String>,
parameter_types: Vec<Arc<TypeBox>>, parameter_types: Vec<Arc<TypeBox>>,
return_type: Arc<TypeBox>, return_type: Arc<TypeBox>,
) -> Self { ) -> Self {
Self { Self {
name, name: name.into(),
parameters, parameters,
parameter_types, parameter_types,
return_type, return_type,
@ -58,7 +58,7 @@ pub struct TypeBox {
pub fields: HashMap<String, Arc<TypeBox>>, pub fields: HashMap<String, Arc<TypeBox>>,
/// メソッドシグニチャ情報 /// メソッドシグニチャ情報
pub methods: HashMap<String, MethodSignature>, pub methods: HashMap<Arc<str>, MethodSignature>,
/// 親型(継承) /// 親型(継承)
pub parent_type: Option<Arc<TypeBox>>, pub parent_type: Option<Arc<TypeBox>>,
@ -295,7 +295,7 @@ impl Display for TypeBox {
#[derive(Debug)] #[derive(Debug)]
pub struct TypeRegistry { pub struct TypeRegistry {
/// 登録済み型 /// 登録済み型
types: HashMap<String, Arc<TypeBox>>, types: HashMap<Arc<str>, Arc<TypeBox>>,
/// 継承チェーン情報(高速化用) /// 継承チェーン情報(高速化用)
inheritance_chains: HashMap<String, Vec<String>>, inheritance_chains: HashMap<String, Vec<String>>,
@ -342,7 +342,7 @@ impl TypeRegistry {
} }
self.inheritance_chains.insert(name_s.clone(), chain); 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<String> { pub fn get_all_type_names(&self) -> Vec<String> {
self.types.keys().cloned().collect() self.types.keys().map(|k| k.as_ref().to_string()).collect()
} }
/// ジェネリクス型をインスタンス化 /// ジェネリクス型をインスタンス化

View File

@ -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"