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>>`.
|
- `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 1–2 weeks)
|
Refactor Plan (next 1–2 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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ジェネリクス型をインスタンス化
|
/// ジェネリクス型をインスタンス化
|
||||||
|
|||||||
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