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:
@ -43,6 +43,9 @@ Clone() Reduction — Phase 1 (Arc/str)
|
||||
- `TypeBox.type_parameters: Vec<String>` → `Vec<Arc<str>>`.
|
||||
- Adjusted builder/registry and `full_name()` accordingly; public behavior unchanged.
|
||||
- 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 1–2 weeks)
|
||||
1) Split parse_box_declaration (667 lines) in 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.<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]`
|
||||
/// 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<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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
self.validate_birth_once_cycles(&methods)?;
|
||||
|
||||
Ok(ASTNode::BoxDeclaration {
|
||||
name,
|
||||
|
||||
@ -14,7 +14,7 @@ use std::sync::Arc;
|
||||
/// メソッドシグニチャ情報
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MethodSignature {
|
||||
pub name: String,
|
||||
pub name: Arc<str>,
|
||||
pub parameters: Vec<String>,
|
||||
pub parameter_types: Vec<Arc<TypeBox>>,
|
||||
pub return_type: Arc<TypeBox>,
|
||||
@ -22,9 +22,9 @@ pub struct 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 {
|
||||
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<N: Into<Arc<str>>>(
|
||||
name: N,
|
||||
parameters: Vec<String>,
|
||||
parameter_types: Vec<Arc<TypeBox>>,
|
||||
return_type: Arc<TypeBox>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
name: name.into(),
|
||||
parameters,
|
||||
parameter_types,
|
||||
return_type,
|
||||
@ -58,7 +58,7 @@ pub struct TypeBox {
|
||||
pub fields: HashMap<String, Arc<TypeBox>>,
|
||||
|
||||
/// メソッドシグニチャ情報
|
||||
pub methods: HashMap<String, MethodSignature>,
|
||||
pub methods: HashMap<Arc<str>, MethodSignature>,
|
||||
|
||||
/// 親型(継承)
|
||||
pub parent_type: Option<Arc<TypeBox>>,
|
||||
@ -295,7 +295,7 @@ impl Display for TypeBox {
|
||||
#[derive(Debug)]
|
||||
pub struct TypeRegistry {
|
||||
/// 登録済み型
|
||||
types: HashMap<String, Arc<TypeBox>>,
|
||||
types: HashMap<Arc<str>, Arc<TypeBox>>,
|
||||
|
||||
/// 継承チェーン情報(高速化用)
|
||||
inheritance_chains: HashMap<String, Vec<String>>,
|
||||
@ -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<String> {
|
||||
self.types.keys().cloned().collect()
|
||||
self.types.keys().map(|k| k.as_ref().to_string()).collect()
|
||||
}
|
||||
|
||||
/// ジェネリクス型をインスタンス化
|
||||
|
||||
20
tools/test/smoke/llvm/ir_phi_empty_check_all.sh
Normal file
20
tools/test/smoke/llvm/ir_phi_empty_check_all.sh
Normal 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"
|
||||
Reference in New Issue
Block a user