feat(joinir): string accumulator emitter (JoinIR)

- Add StringAccumulatorEmitter in join_ir/lowering/common/
- Emit string concat as BinOp(Add) for polymorphic VM/LLVM handling
- Ensure VM/LLVM same semantics
- Fail-Fast: RHS must be Variable (not Literal/MethodCall)
- Pattern2 wiring: string carrier昇格 + type refinement + validation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-17 16:33:18 +09:00
parent ad072e5e09
commit 27fd9720d0
3 changed files with 194 additions and 1 deletions

View File

@ -186,8 +186,9 @@ fn prepare_pattern2_inputs(
// Phase 100 P2-2: Mutable Accumulator Analysis
// Detect accumulator pattern (target = target + x) and promote to carrier
// Phase 100 P3-3: Added AccumulatorKind support for string accumulators
use crate::mir::loop_pattern_detection::mutable_accumulator_analyzer::{
MutableAccumulatorAnalyzer, RhsExprKind,
AccumulatorKind, MutableAccumulatorAnalyzer, RhsExprKind,
};
let mutable_spec = MutableAccumulatorAnalyzer::analyze(body)?;
@ -221,6 +222,81 @@ fn prepare_pattern2_inputs(
)
})?;
// Phase 100 P3-3: Refine AccumulatorKind based on actual target type
// AST-only detection (P3-1) defaults to Int for Variables
// Here we check actual type from builder's type context
let mut refined_kind = spec.kind; // Start with AST-detected kind
if spec.rhs_expr_kind == RhsExprKind::Var && refined_kind == AccumulatorKind::Int {
// Check if target is string-typed (refine Int → String if needed)
use crate::mir::MirType;
if let Some(target_type) = builder.type_ctx.value_types.get(target_id) {
match target_type {
MirType::Box(box_name) if box_name == "StringBox" => {
refined_kind = AccumulatorKind::String;
if verbose {
log.log(
"phase100_p3",
format!(
"Refined accumulator kind: Int → String (target '{}' is StringBox)",
spec.target_name
),
);
}
}
MirType::Integer => {
// Confirmed Int
if verbose {
log.log(
"phase100_p3",
format!(
"Confirmed accumulator kind: Int (target '{}' is Integer)",
spec.target_name
),
);
}
}
_ => {
// Unknown type - keep default Int for backward compat
if verbose {
log.log(
"phase100_p3",
format!(
"Accumulator kind: Int (default, target '{}' type unknown: {:?})",
spec.target_name, target_type
),
);
}
}
}
}
}
// Phase 100 P3-3: Validate String accumulator constraints
if refined_kind == AccumulatorKind::String {
// String accumulator RHS must be Variable (not Literal, not MethodCall)
match spec.rhs_expr_kind {
RhsExprKind::Var => {
// OK: Variable RHS is allowed for String accumulators
if verbose {
log.log(
"phase100_p3",
format!(
"String accumulator '{}' = '{}' + '{}' (Variable RHS: OK)",
spec.target_name, spec.target_name, spec.rhs_var_or_lit
),
);
}
}
RhsExprKind::Literal => {
// Fail-Fast: Literal RHS not supported in P3 (will be P3.1)
return Err(format!(
"[joinir/mutable-acc] String accumulator '{}' with Literal RHS not supported in Phase 100 P3 (will be P3.1)",
spec.target_name
));
}
}
}
// Check RHS read-only via captured_env lookup
// According to spec: x ∈ {Const, BodyLocal, Captured, Pinned, Carrier}
// At this point in prepare_pattern2_inputs: