feat(joinir): Phase 188 StringAppend support in Pattern2/4
- Extended Pattern2/4 whitelist to accept StringLiteral updates - CarrierUpdateEmitter now emits JoinIR for string append - Selective Fail-Fast: accept safe patterns, reject complex Changes: - pattern2_with_break.rs: StringLiteral whitelist - pattern4_with_continue.rs: StringLiteral whitelist - carrier_update_emitter.rs: StringLiteral JoinIR emission Tests: - phase188_string_append_char.hako - phase188_string_append_literal.hako - 10/10 carrier_update_emitter tests PASS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -198,10 +198,15 @@
|
||||
- Complex (method call / nested BinOp) は Fail-Fast 維持
|
||||
- Phase 178 の Fail-Fast は完全保持
|
||||
- Phase 188+ での実装方針を確立
|
||||
- [ ] Phase 188+: StringAppendChar/Literal 実装
|
||||
- Pattern2/4 lowerer の can_lower() whitelist 拡張
|
||||
- CarrierUpdateLowerer の string append 対応
|
||||
- _parse_number minimal 版の E2E テスト
|
||||
- [x] **Phase 188: StringAppend 実装** ✅ (2025-12-09)
|
||||
- Task 188-1: LoopUpdateAnalyzer 拡張(既存の UpdateRhs で対応済み)
|
||||
- Task 188-2: Pattern2/4 can_lower ホワイトリスト更新(UpdateRhs::Other のみ拒否)
|
||||
- Task 188-3: CarrierUpdateEmitter JoinIR emission 拡張(StringLiteral → Const emission)
|
||||
- Task 188-4: E2E テスト(phase188_string_append_char.hako / phase188_string_append_literal.hako)
|
||||
- Task 188-5: ドキュメント更新(phase187-string-update-design.md + joinir-architecture-overview.md)
|
||||
- **成果**: Pattern2/4 が安全な string 更新パターンを受理。Complex パターンのみ Fail-Fast。
|
||||
- 許可: `s = s + ch`, `s = s + "literal"`
|
||||
- 拒否: `s = s + s.substring(...)` (method call), `s = s + (a + b)` (nested BinOp)
|
||||
- [ ] Phase 185+: Body-local Pattern2/4 統合 + String ops 有効化
|
||||
- Pattern2/4 lowerer に LoopBodyLocalEnv 統合
|
||||
- body-local 変数(`local temp` in loop body)の完全 MIR 生成対応
|
||||
|
||||
19
apps/tests/phase188_string_append_char.hako
Normal file
19
apps/tests/phase188_string_append_char.hako
Normal file
@ -0,0 +1,19 @@
|
||||
// Phase 188: String append with character variable
|
||||
// Pattern: s = s + ch (StringAppendChar)
|
||||
// Pattern 2: Loop with conditional break
|
||||
|
||||
static box SAppendChar {
|
||||
method main() {
|
||||
local s = ""
|
||||
local i = 0
|
||||
loop(i < 10) {
|
||||
if i == 3 {
|
||||
break
|
||||
}
|
||||
local ch = "x"
|
||||
s = s + ch
|
||||
i = i + 1
|
||||
}
|
||||
print(s) // Expected: "xxx"
|
||||
}
|
||||
}
|
||||
18
apps/tests/phase188_string_append_literal.hako
Normal file
18
apps/tests/phase188_string_append_literal.hako
Normal file
@ -0,0 +1,18 @@
|
||||
// Phase 188: String append with literal
|
||||
// Pattern: s = s + "y" (StringAppendLiteral)
|
||||
// Pattern 4: Loop with continue
|
||||
|
||||
static box SAppendLit {
|
||||
method main() {
|
||||
local s = ""
|
||||
local i = 0
|
||||
loop(i < 5) {
|
||||
i = i + 1
|
||||
if i > 3 {
|
||||
continue
|
||||
}
|
||||
s = s + "y"
|
||||
}
|
||||
print(s) // Expected: "yyy"
|
||||
}
|
||||
}
|
||||
@ -188,14 +188,18 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
|
||||
|
||||
### 2.3 キャリア / Exit / Boundary ライン
|
||||
|
||||
- **CarrierInfo / LoopUpdateAnalyzer**
|
||||
- **CarrierInfo / LoopUpdateAnalyzer / CarrierUpdateEmitter**
|
||||
- ファイル:
|
||||
- `src/mir/join_ir/lowering/carrier_info.rs`
|
||||
- `src/mir/join_ir/lowering/loop_update_analyzer.rs`
|
||||
- `src/mir/join_ir/lowering/carrier_update_emitter.rs`
|
||||
- 責務:
|
||||
- ループで更新される変数(carrier)を検出し、UpdateExpr を保持。
|
||||
- Pattern 4 では実際に更新されるキャリアだけを残す。
|
||||
- **Phase 187設計**: String 更新は UpdateKind ベースのホワイトリストで扱う方針(StringAppendChar/Literal は Phase 188+ で実装予定)。
|
||||
- **Phase 188 完了** ✅: String 更新(StringAppendChar/StringAppendLiteral)を UpdateRhs ベースのホワイトリストで受理し、JoinIR BinOp を emit。
|
||||
- 許可: `UpdateRhs::Const`, `UpdateRhs::Variable`, `UpdateRhs::StringLiteral`
|
||||
- 拒否: `UpdateRhs::Other`(method call / nested BinOp 等の複雑パターンのみ)
|
||||
- Pattern2/4 の can_lower() で選別、carrier_update_emitter.rs で JoinIR 生成
|
||||
|
||||
- **ExitMeta / JoinFragmentMeta**
|
||||
- ファイル: `carrier_info.rs`
|
||||
|
||||
@ -356,6 +356,48 @@ Add Phase 187 entry:
|
||||
|
||||
---
|
||||
|
||||
## Phase 188 Implementation Complete (2025-12-09)
|
||||
|
||||
### Implementation Summary
|
||||
|
||||
Phase 188 successfully implemented StringAppendChar and StringAppendLiteral support in JoinIR patterns.
|
||||
|
||||
**Changes Made**:
|
||||
|
||||
1. **Pattern2/4 `can_lower()` Whitelist** (Task 188-2)
|
||||
- Updated `pattern2_with_break.rs` and `pattern4_with_continue.rs`
|
||||
- Allow: `UpdateRhs::Const`, `UpdateRhs::Variable`, `UpdateRhs::StringLiteral`
|
||||
- Reject: `UpdateRhs::Other` (complex updates only)
|
||||
- Old behavior: Rejected all string updates
|
||||
- New behavior: Accept safe string patterns, reject only complex ones
|
||||
|
||||
2. **CarrierUpdateLowerer JoinIR Emission** (Task 188-3)
|
||||
- Updated `carrier_update_emitter.rs` (both UpdateEnv and ConditionEnv versions)
|
||||
- `UpdateRhs::StringLiteral(s)` → Emit `Const { value: ConstValue::String(s) }` + `BinOp`
|
||||
- `UpdateRhs::Variable(name)` → Resolve variable, emit `BinOp` (handles both numeric and string)
|
||||
- `UpdateRhs::Other` → Return error (should be caught by can_lower)
|
||||
|
||||
3. **E2E Test Files** (Task 188-4)
|
||||
- Created `apps/tests/phase188_string_append_char.hako` (Pattern 2 with break)
|
||||
- Created `apps/tests/phase188_string_append_literal.hako` (Pattern 4 with continue)
|
||||
- Both tests compile and run without errors
|
||||
- JoinIR generation succeeds for both patterns
|
||||
|
||||
**Verification**:
|
||||
```bash
|
||||
# Pattern 2: StringAppendChar
|
||||
NYASH_JOINIR_CORE=1 ./target/release/hakorune apps/tests/phase188_string_append_char.hako
|
||||
# Output: Pattern 2 triggered, JoinIR generated successfully
|
||||
|
||||
# Pattern 4: StringAppendLiteral
|
||||
NYASH_JOINIR_CORE=1 ./target/release/hakorune apps/tests/phase188_string_append_literal.hako
|
||||
# Output: Pattern 4 triggered, JoinIR generated successfully
|
||||
```
|
||||
|
||||
**Key Achievement**: Phase 178's Fail-Fast is now selective - only rejects truly complex updates (method calls, nested BinOp), while allowing safe string concatenation patterns.
|
||||
|
||||
---
|
||||
|
||||
## 10. Conclusion
|
||||
|
||||
Phase 187 establishes a clear design for string update handling in JoinIR:
|
||||
|
||||
@ -57,7 +57,8 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Phase 178: Check for string/complex carrier updates
|
||||
// Phase 178/188: Check for complex carrier updates
|
||||
// Phase 188: StringAppendChar/StringAppendLiteral are now allowed
|
||||
// Create dummy carriers from body assignment targets for analysis
|
||||
let dummy_carriers: Vec<CarrierVar> = ctx.body.iter().filter_map(|node| {
|
||||
match node {
|
||||
@ -78,15 +79,27 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
|
||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(ctx.body, &dummy_carriers);
|
||||
|
||||
// Check if any update is string/complex
|
||||
// Phase 188: Check if any update is complex (reject only UpdateRhs::Other)
|
||||
// Allow: Const (numeric), Variable (numeric/string), StringLiteral
|
||||
// Reject: Other (method calls, nested BinOp)
|
||||
for update in updates.values() {
|
||||
if let UpdateExpr::BinOp { rhs, .. } = update {
|
||||
match rhs {
|
||||
UpdateRhs::StringLiteral(_) | UpdateRhs::Other => {
|
||||
eprintln!("[pattern2/can_lower] Phase 178: String/complex update detected, rejecting Pattern 2 (unsupported)");
|
||||
UpdateRhs::Const(_) => {
|
||||
// Numeric: i = i + 1 (allowed)
|
||||
}
|
||||
UpdateRhs::Variable(_) => {
|
||||
// Phase 188: StringAppendChar: s = s + ch (allowed)
|
||||
// Or numeric: sum = sum + i (allowed)
|
||||
}
|
||||
UpdateRhs::StringLiteral(_) => {
|
||||
// Phase 188: StringAppendLiteral: s = s + "x" (allowed)
|
||||
}
|
||||
UpdateRhs::Other => {
|
||||
// Phase 188: Complex update (method call, nested BinOp) - reject
|
||||
eprintln!("[pattern2/can_lower] Phase 188: Complex update detected (UpdateRhs::Other), rejecting Pattern 2");
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +70,8 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Phase 178: Check for string/complex carrier updates
|
||||
// Phase 178/188: Check for complex carrier updates
|
||||
// Phase 188: StringAppendChar/StringAppendLiteral are now allowed
|
||||
// Create dummy carriers from body assignment targets for analysis
|
||||
let dummy_carriers: Vec<CarrierVar> = ctx.body.iter().filter_map(|node| {
|
||||
match node {
|
||||
@ -91,15 +92,27 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
|
||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(ctx.body, &dummy_carriers);
|
||||
|
||||
// Check if any update is string/complex
|
||||
// Phase 188: Check if any update is complex (reject only UpdateRhs::Other)
|
||||
// Allow: Const (numeric), Variable (numeric/string), StringLiteral
|
||||
// Reject: Other (method calls, nested BinOp)
|
||||
for update in updates.values() {
|
||||
if let UpdateExpr::BinOp { rhs, .. } = update {
|
||||
match rhs {
|
||||
UpdateRhs::StringLiteral(_) | UpdateRhs::Other => {
|
||||
eprintln!("[pattern4/can_lower] Phase 178: String/complex update detected, rejecting Pattern 4 (unsupported)");
|
||||
UpdateRhs::Const(_) => {
|
||||
// Numeric: i = i + 1 (allowed)
|
||||
}
|
||||
UpdateRhs::Variable(_) => {
|
||||
// Phase 188: StringAppendChar: s = s + ch (allowed)
|
||||
// Or numeric: sum = sum + i (allowed)
|
||||
}
|
||||
UpdateRhs::StringLiteral(_) => {
|
||||
// Phase 188: StringAppendLiteral: s = s + "x" (allowed)
|
||||
}
|
||||
UpdateRhs::Other => {
|
||||
// Phase 188: Complex update (method call, nested BinOp) - reject
|
||||
eprintln!("[pattern4/can_lower] Phase 188: Complex update detected (UpdateRhs::Other), rejecting Pattern 4");
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,15 +124,22 @@ pub fn emit_carrier_update_with_env(
|
||||
)
|
||||
})?
|
||||
}
|
||||
// Phase 178: String updates detected but not lowered to JoinIR yet
|
||||
// The Rust MIR path handles string concatenation
|
||||
// For JoinIR: just pass through the carrier param (no JoinIR update)
|
||||
UpdateRhs::StringLiteral(_) | UpdateRhs::Other => {
|
||||
eprintln!(
|
||||
"[joinir/pattern2] Phase 178: Carrier '{}' has string/complex update - skipping JoinIR emit, using param passthrough",
|
||||
// Phase 188: String updates now emit JoinIR BinOp
|
||||
// StringAppendLiteral: s = s + "literal"
|
||||
UpdateRhs::StringLiteral(s) => {
|
||||
let const_id = alloc_value();
|
||||
instructions.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_id,
|
||||
value: ConstValue::String(s.clone()),
|
||||
}));
|
||||
const_id
|
||||
}
|
||||
// Phase 178/188: Complex updates (method calls) still rejected
|
||||
UpdateRhs::Other => {
|
||||
return Err(format!(
|
||||
"Carrier '{}' has complex update (UpdateRhs::Other) - should be rejected by can_lower()",
|
||||
carrier.name
|
||||
);
|
||||
return Ok(carrier_param); // Pass-through: no JoinIR update
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
@ -259,15 +266,22 @@ pub fn emit_carrier_update(
|
||||
)
|
||||
})?
|
||||
}
|
||||
// Phase 178: String updates detected but not lowered to JoinIR yet
|
||||
// The Rust MIR path handles string concatenation
|
||||
// For JoinIR: just pass through the carrier param (no JoinIR update)
|
||||
UpdateRhs::StringLiteral(_) | UpdateRhs::Other => {
|
||||
eprintln!(
|
||||
"[joinir/pattern2] Phase 178: Carrier '{}' has string/complex update - skipping JoinIR emit, using param passthrough",
|
||||
// Phase 188: String updates now emit JoinIR BinOp
|
||||
// StringAppendLiteral: s = s + "literal"
|
||||
UpdateRhs::StringLiteral(s) => {
|
||||
let const_id = alloc_value();
|
||||
instructions.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_id,
|
||||
value: ConstValue::String(s.clone()),
|
||||
}));
|
||||
const_id
|
||||
}
|
||||
// Phase 178/188: Complex updates (method calls) still rejected
|
||||
UpdateRhs::Other => {
|
||||
return Err(format!(
|
||||
"Carrier '{}' has complex update (UpdateRhs::Other) - should be rejected by can_lower()",
|
||||
carrier.name
|
||||
);
|
||||
return Ok(carrier_param); // Pass-through: no JoinIR update
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user