docs(joinir): Phase 56 - Ownership-Relay Design + interface skeleton
「読むのは自由、管理は直下 owned だけ」アーキテクチャの設計文書と型定義。 Key changes: - Design doc: phase56-ownership-relay-design.md - Core definitions: owned/carriers/captures/relay - Invariants: Ownership Uniqueness, Carrier Locality, Relay Propagation - Shadowing rules, multi-writer merge semantics - JoinIR mapping from current system to new system - Implementation phases roadmap (56-61) - New module: src/mir/join_ir/ownership/ - types.rs: ScopeId, ScopeOwnedVar, RelayVar, CapturedVar, OwnershipPlan - mod.rs: Module documentation with responsibility boundaries - README.md: Usage guide and examples - API methods: - OwnershipPlan::carriers() - owned AND written variables - OwnershipPlan::condition_only_carriers() - condition-only carriers - OwnershipPlan::verify_invariants() - invariant checking Tests: 942/942 PASS (+3 unit tests) Zero behavioral change - analysis module skeleton only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -216,10 +216,21 @@
|
||||
- Phase 53 で実戦ループ追加済みのため、追加投入より先に構造判定精度の測定に集中。
|
||||
- 構造シグネチャ軸を 5+ に拡張(Compare op 分布など)し、P2/P3 の偽陽性観測テストを追加。
|
||||
- 結果: selfhost 群の構造判定だけでは分離が不十分(偽陽性率 ~50%)。dev-only name ガードは当面必須と判断。
|
||||
12. **Phase 55-SELFHOST-SHAPE-AXIS-EXPAND(次のフォーカス候補・dev-only)**: 構造軸 8+ へ拡張し誤判定を下げる足場
|
||||
- 条件複雑度(ネスト/論理結合の形)、算術更新パターン、分岐ファンアウトなどの新軸を追加。
|
||||
- selfhost/canonical の feature ベクトル比較と観測テストを拡充し、name ガード縮小の根拠を作る(撤去は後続)。
|
||||
13. JoinIR Verify / 最適化まわり
|
||||
12. **Phase 55-SELFHOST-SHAPE-AXIS-EXPAND(dev-only / 保留)**: 構造軸を可変 feature として拡張し誤判定を下げる足場
|
||||
- Phase 56–61 の Ownership-Relay ライン優先のため、selfhost shape 軸拡張は一旦保留。
|
||||
- OwnershipAnalyzer 導入後に、scope 署名(owned/carriers/captures/relay)を新しい構造軸として合流させる。
|
||||
13. **Phase 56-OWNERSHIP-RELAY-DESIGN(完了✅ 2025-12-12)**: Ownership-Relay アーキテクチャ設計 + インターフェース skeleton
|
||||
- 設計詳細: [phase56-ownership-relay-design.md](docs/development/current/main/phase56-ownership-relay-design.md)
|
||||
- コア定義: owned / carriers / captures / relay の 4 分類を明確化
|
||||
- 不変条件: Ownership Uniqueness / Carrier Locality / Relay Propagation / Capture Read-Only
|
||||
- Module 作成: `src/mir/join_ir/ownership/` - 責務は「解析のみ」
|
||||
- 型定義: `ScopeId`, `ScopeOwnedVar`, `RelayVar`, `CapturedVar`, `OwnershipPlan`
|
||||
- テスト: 3 つのユニットテスト追加(empty plan / carriers filter / invariant verification)
|
||||
- 次: Phase 57 で OwnershipAnalyzer 実装(dev-only)
|
||||
14. **Phase 57-OWNERSHIP-ANALYZER-DEV(次のフォーカス候補・dev-only)**: OwnershipPlan を生成する解析箱の実装
|
||||
- `OwnershipAnalyzer` を追加し、ネスト含む reads/writes/owned を集計→ carriers/relay/captures を plan 化。
|
||||
- 既存 fixtures(pattern2/3, jsonparser, selfhost)で plan の回帰テストを追加。
|
||||
15. JoinIR Verify / 最適化まわり
|
||||
- すでに PHI/ValueId 契約は debug ビルドで検証しているので、
|
||||
必要なら SSA‑DFA や軽い最適化(Loop invariant / Strength reduction)を検討。
|
||||
|
||||
|
||||
224
docs/development/current/main/phase56-ownership-relay-design.md
Normal file
224
docs/development/current/main/phase56-ownership-relay-design.md
Normal file
@ -0,0 +1,224 @@
|
||||
# Phase 56: Ownership-Relay Design
|
||||
|
||||
## Overview
|
||||
「読むのは自由、管理は直下 owned だけ」アーキテクチャの設計文書。
|
||||
|
||||
Phase 56 は **インターフェース設計のみ**。実装は Phase 57 以降。
|
||||
|
||||
## Core Definitions
|
||||
|
||||
### owned (所有)
|
||||
- 変数を定義したスコープが唯一の owner
|
||||
- Loop直下の `local x` → そのループが owned
|
||||
- body-local(if/block内の local)→ 最も内側の enclosing loop が owned
|
||||
|
||||
**例**:
|
||||
```rust
|
||||
fn outer() {
|
||||
local a = 0 // owned by outer
|
||||
loop {
|
||||
local b = 0 // owned by this loop
|
||||
if cond {
|
||||
local c = 0 // owned by enclosing loop (not if!)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### carriers (管理対象)
|
||||
- `carriers = writes ∩ owned`
|
||||
- そのスコープが定義 AND 更新する変数のみ
|
||||
- loop_step の引数として管理
|
||||
|
||||
**重要**: Carrier は「所有 AND 更新」のみ。読み取り専用の owned 変数は carrier ではない。
|
||||
|
||||
### captures (読み取り参照)
|
||||
- `captures = reads \ owned` (かつ carriers ではない)
|
||||
- 祖先スコープの変数への read-only アクセス
|
||||
- CapturedEnv / ConditionEnv 経由
|
||||
|
||||
**例**:
|
||||
```rust
|
||||
local limit = 100
|
||||
loop {
|
||||
local sum = 0
|
||||
if sum < limit { // limit は capture (read-only)
|
||||
sum++ // sum は carrier (owned + written)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### relay (中継)
|
||||
- 内側スコープが祖先 owned を更新する場合
|
||||
- 更新責務を owner へ昇格(relay up)
|
||||
- 中間ループは引数として素通し
|
||||
|
||||
**例**:
|
||||
```rust
|
||||
loop outer {
|
||||
local total = 0 // owned by outer
|
||||
loop inner {
|
||||
total++ // relay to outer (inner doesn't own total)
|
||||
}
|
||||
}
|
||||
// outer の exit PHI で total を merge
|
||||
```
|
||||
|
||||
## Invariants (不変条件)
|
||||
|
||||
1. **Ownership Uniqueness**: 各変数は唯一の owner を持つ
|
||||
2. **Carrier Locality**: carriers = writes ∩ owned (借用なし)
|
||||
3. **Relay Propagation**: writes \ owned → owner に昇格
|
||||
4. **Capture Read-Only**: captures は read-only (PHI 不要)
|
||||
|
||||
## Shadowing Rules
|
||||
|
||||
```nyash
|
||||
local x = 0 // outer owned
|
||||
loop {
|
||||
local x = 1 // inner owned (shadows outer)
|
||||
// 外の x は capture 可能だが、inner の x が優先
|
||||
print(x) // inner の x (1)
|
||||
}
|
||||
print(x) // outer の x (0)
|
||||
```
|
||||
|
||||
- Shadowing = 新しい ownership 発生
|
||||
- 名前解決は最内スコープ優先
|
||||
- 外の x は capture として参照可能だが、内の x が存在する限り内が優先
|
||||
|
||||
## Multi-Writer Merge
|
||||
|
||||
```nyash
|
||||
loop outer {
|
||||
local total = 0
|
||||
if a { loop inner1 { total++ } } // relay to outer
|
||||
if b { loop inner2 { total-- } } // relay to outer
|
||||
}
|
||||
// outer の exit PHI で merge
|
||||
```
|
||||
|
||||
- Relay は「更新意図の伝達」
|
||||
- 実際の PHI merge は owner 側で実行
|
||||
- 複数の内側ループが同じ変数を relay → owner の exit PHI で統合
|
||||
|
||||
## JoinIR Mapping
|
||||
|
||||
### Current System → New System
|
||||
|
||||
| Current | New |
|
||||
|---------|-----|
|
||||
| CarrierInfo.carriers | OwnershipPlan.owned_carriers |
|
||||
| promoted_loopbodylocals | (absorbed into owned analysis) |
|
||||
| CapturedEnv | OwnershipPlan.captures |
|
||||
| ConditionEnv | OwnershipPlan.condition_captures |
|
||||
| (implicit) | OwnershipPlan.relay_writes |
|
||||
|
||||
### OwnershipPlan Structure
|
||||
|
||||
```rust
|
||||
pub struct OwnershipPlan {
|
||||
pub scope_id: ScopeId,
|
||||
pub owned_carriers: Vec<ScopeOwnedVar>,
|
||||
pub relay_writes: Vec<RelayVar>,
|
||||
pub captures: Vec<CapturedVar>,
|
||||
pub condition_captures: Vec<CapturedVar>,
|
||||
}
|
||||
```
|
||||
|
||||
**設計意図**:
|
||||
- `owned_carriers`: このスコープが所有 AND 更新する変数
|
||||
- `relay_writes`: 祖先の変数への書き込み(owner へ昇格)
|
||||
- `captures`: 祖先の変数への読み取り専用参照
|
||||
- `condition_captures`: captures のうち、条件式で使われるもの
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
- **Phase 56**: Design + interface skeleton (this phase) ✅
|
||||
- **Phase 57**: OwnershipAnalyzer implementation (dev-only)
|
||||
- **Phase 58**: P2 plumbing (dev-only)
|
||||
- **Phase 59**: P3 plumbing (dev-only)
|
||||
- **Phase 60**: Cleanup dev heuristics
|
||||
- **Phase 61**: Canonical promotion decision
|
||||
|
||||
## Module Boundary
|
||||
|
||||
`src/mir/join_ir/ownership/` - 責務は「解析のみ」
|
||||
|
||||
**This module does**:
|
||||
- ✅ Collect reads/writes from AST/ProgramJSON
|
||||
- ✅ Determine variable ownership (owned/relay/capture)
|
||||
- ✅ Produce OwnershipPlan for downstream lowering
|
||||
|
||||
**This module does NOT**:
|
||||
- ❌ Generate MIR instructions
|
||||
- ❌ Modify JoinIR structures
|
||||
- ❌ Perform lowering transformations
|
||||
|
||||
Lowering/MIR生成は既存モジュールが担当。
|
||||
|
||||
## Example Ownership Plans
|
||||
|
||||
### Example 1: Simple Loop
|
||||
|
||||
```nyash
|
||||
local sum = 0
|
||||
loop {
|
||||
sum++
|
||||
}
|
||||
```
|
||||
|
||||
**OwnershipPlan (loop scope)**:
|
||||
- `owned_carriers`: [`sum` (written)]
|
||||
- `relay_writes`: []
|
||||
- `captures`: []
|
||||
|
||||
### Example 2: Nested Loop with Relay
|
||||
|
||||
```nyash
|
||||
local total = 0
|
||||
loop outer {
|
||||
loop inner {
|
||||
total++
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**OwnershipPlan (inner loop)**:
|
||||
- `owned_carriers`: []
|
||||
- `relay_writes`: [`total` → relay to outer]
|
||||
- `captures`: []
|
||||
|
||||
**OwnershipPlan (outer loop)**:
|
||||
- `owned_carriers`: [`total` (written via relay)]
|
||||
- `relay_writes`: []
|
||||
- `captures`: []
|
||||
|
||||
### Example 3: Capture + Carrier
|
||||
|
||||
```nyash
|
||||
local limit = 100
|
||||
loop {
|
||||
local sum = 0
|
||||
if sum < limit {
|
||||
sum++
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**OwnershipPlan (loop scope)**:
|
||||
- `owned_carriers`: [`sum` (written)]
|
||||
- `relay_writes`: []
|
||||
- `captures`: [`limit` (read-only)]
|
||||
- `condition_captures`: [`limit`]
|
||||
|
||||
## References
|
||||
- **Phase 53-54**: Structural axis expansion
|
||||
- **Phase 43/245B**: Normalized JoinIR completion
|
||||
- **ChatGPT discussion**: 「読むのは自由、管理は直下だけ」設計
|
||||
- **JoinIR Architecture**: [joinir-architecture-overview.md](joinir-architecture-overview.md)
|
||||
|
||||
## Status
|
||||
|
||||
- ✅ Phase 56 (this): Design + interface skeleton completed
|
||||
- ⏳ Phase 57+: Implementation pending
|
||||
@ -41,6 +41,9 @@ pub mod normalized;
|
||||
// Phase 34-1: Frontend (AST→JoinIR) — skeleton only
|
||||
pub mod frontend;
|
||||
|
||||
// Phase 56: Ownership analysis (reads/writes → owned/relay/capture)
|
||||
pub mod ownership;
|
||||
|
||||
// Re-export lowering functions for backward compatibility
|
||||
pub use lowering::{
|
||||
lower_funcscanner_trim_to_joinir, lower_min_loop_to_joinir, lower_skip_ws_to_joinir,
|
||||
|
||||
79
src/mir/join_ir/ownership/README.md
Normal file
79
src/mir/join_ir/ownership/README.md
Normal file
@ -0,0 +1,79 @@
|
||||
# Ownership Analysis Module
|
||||
|
||||
## Responsibility Boundary
|
||||
|
||||
This module is responsible for **analysis only**:
|
||||
- ✅ Collecting reads/writes from AST/ProgramJSON
|
||||
- ✅ Determining variable ownership (owned/relay/capture)
|
||||
- ✅ Producing OwnershipPlan for downstream lowering
|
||||
|
||||
This module does NOT:
|
||||
- ❌ Generate MIR instructions
|
||||
- ❌ Modify JoinIR structures
|
||||
- ❌ Perform lowering transformations
|
||||
|
||||
## Core Types
|
||||
|
||||
| Type | Purpose |
|
||||
|------|---------|
|
||||
| `ScopeId` | Unique scope identifier |
|
||||
| `ScopeOwnedVar` | Variable defined in this scope |
|
||||
| `RelayVar` | Write to ancestor-owned variable |
|
||||
| `CapturedVar` | Read-only reference to ancestor |
|
||||
| `OwnershipPlan` | Complete analysis result |
|
||||
|
||||
## Invariants
|
||||
|
||||
1. `carriers = owned_vars.filter(is_written)`
|
||||
2. No variable in both owned and relay
|
||||
3. No variable in both owned and captures
|
||||
4. Relay implies ancestor ownership exists
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
**「読むのは自由、管理は直下 owned だけ」**
|
||||
|
||||
- **Owned**: Variable defined in this scope (unique owner)
|
||||
- **Carrier**: Owned AND written (managed as loop_step argument)
|
||||
- **Capture**: Read-only reference to ancestor (via CapturedEnv)
|
||||
- **Relay**: Write to ancestor → relay up to owner (exit PHI at owner)
|
||||
|
||||
## Phase Status
|
||||
|
||||
- Phase 56: ✅ Interface skeleton
|
||||
- Phase 57: ⏳ OwnershipAnalyzer implementation
|
||||
- Phase 58: ⏳ P2 plumbing
|
||||
- Phase 59: ⏳ P3 plumbing
|
||||
- Phase 60: ⏳ Cleanup dev heuristics
|
||||
- Phase 61: ⏳ Canonical promotion decision
|
||||
|
||||
## Usage (Future)
|
||||
|
||||
```rust
|
||||
let plan = OwnershipAnalyzer::analyze(&ast_node, parent_scope);
|
||||
plan.verify_invariants()?;
|
||||
let carriers: Vec<_> = plan.carriers().collect();
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```nyash
|
||||
local limit = 100 // owned by outer
|
||||
loop {
|
||||
local sum = 0 // owned by loop
|
||||
if sum < limit { // limit = capture (read-only)
|
||||
sum++ // sum = carrier (owned + written)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**OwnershipPlan (loop scope)**:
|
||||
- `owned_vars`: [`sum` (written), `limit` (read-only)]
|
||||
- `relay_writes`: []
|
||||
- `captures`: [`limit`]
|
||||
- `condition_captures`: [`limit`]
|
||||
|
||||
## References
|
||||
|
||||
- Design Doc: [phase56-ownership-relay-design.md](../../../../docs/development/current/main/phase56-ownership-relay-design.md)
|
||||
- JoinIR Architecture: [joinir-architecture-overview.md](../../../../docs/development/current/main/joinir-architecture-overview.md)
|
||||
28
src/mir/join_ir/ownership/mod.rs
Normal file
28
src/mir/join_ir/ownership/mod.rs
Normal file
@ -0,0 +1,28 @@
|
||||
//! Ownership Analysis for JoinIR
|
||||
//!
|
||||
//! # Responsibility Boundary
|
||||
//!
|
||||
//! This module is responsible for **analysis only**:
|
||||
//! - Collecting reads/writes from AST/ProgramJSON
|
||||
//! - Determining variable ownership (owned/relay/capture)
|
||||
//! - Producing OwnershipPlan for downstream lowering
|
||||
//!
|
||||
//! This module does NOT:
|
||||
//! - Generate MIR instructions
|
||||
//! - Modify JoinIR structures
|
||||
//! - Perform lowering transformations
|
||||
//!
|
||||
//! # Core Invariants
|
||||
//!
|
||||
//! 1. **Ownership Uniqueness**: Each variable has exactly one owner scope
|
||||
//! 2. **Carrier Locality**: carriers = writes ∩ owned
|
||||
//! 3. **Relay Propagation**: writes to ancestor-owned → relay up
|
||||
//! 4. **Capture Read-Only**: captures have no PHI at this scope
|
||||
//!
|
||||
//! # Phase 56 Status
|
||||
//!
|
||||
//! Interface skeleton only. Implementation in Phase 57+.
|
||||
|
||||
mod types;
|
||||
|
||||
pub use types::*;
|
||||
168
src/mir/join_ir/ownership/types.rs
Normal file
168
src/mir/join_ir/ownership/types.rs
Normal file
@ -0,0 +1,168 @@
|
||||
//! Core types for ownership analysis.
|
||||
//!
|
||||
//! Phase 56: Interface definitions only (not yet used).
|
||||
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
/// Unique identifier for a scope (loop, function, block).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct ScopeId(pub u32);
|
||||
|
||||
/// A variable owned by the current scope.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ScopeOwnedVar {
|
||||
/// Variable name
|
||||
pub name: String,
|
||||
/// Whether this variable is written within the scope
|
||||
pub is_written: bool,
|
||||
/// Whether this variable is used in loop conditions
|
||||
pub is_condition_only: bool,
|
||||
}
|
||||
|
||||
/// A variable whose updates should be relayed to an ancestor owner.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RelayVar {
|
||||
/// Variable name
|
||||
pub name: String,
|
||||
/// Scope that owns this variable
|
||||
pub owner_scope: ScopeId,
|
||||
/// Intermediate scopes that need to forward this update
|
||||
pub relay_path: Vec<ScopeId>,
|
||||
}
|
||||
|
||||
/// A variable captured (read-only) from an ancestor scope.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CapturedVar {
|
||||
/// Variable name
|
||||
pub name: String,
|
||||
/// Scope that owns this variable
|
||||
pub owner_scope: ScopeId,
|
||||
}
|
||||
|
||||
/// Complete ownership analysis result for a scope.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OwnershipPlan {
|
||||
/// ID of this scope
|
||||
pub scope_id: ScopeId,
|
||||
|
||||
/// Variables owned by this scope (defined here)
|
||||
/// Invariant: carriers = owned_vars where is_written = true
|
||||
pub owned_vars: Vec<ScopeOwnedVar>,
|
||||
|
||||
/// Variables written but owned by ancestor (need relay)
|
||||
pub relay_writes: Vec<RelayVar>,
|
||||
|
||||
/// Variables read but not owned (read-only capture)
|
||||
pub captures: Vec<CapturedVar>,
|
||||
|
||||
/// Subset of captures used in conditions
|
||||
pub condition_captures: Vec<CapturedVar>,
|
||||
}
|
||||
|
||||
impl Default for ScopeId {
|
||||
fn default() -> Self {
|
||||
ScopeId(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl OwnershipPlan {
|
||||
/// Create empty plan for a scope.
|
||||
pub fn new(scope_id: ScopeId) -> Self {
|
||||
Self {
|
||||
scope_id,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get carriers (owned AND written).
|
||||
pub fn carriers(&self) -> impl Iterator<Item = &ScopeOwnedVar> {
|
||||
self.owned_vars.iter().filter(|v| v.is_written)
|
||||
}
|
||||
|
||||
/// Get condition-only carriers (owned, written, condition-only).
|
||||
pub fn condition_only_carriers(&self) -> impl Iterator<Item = &ScopeOwnedVar> {
|
||||
self.owned_vars.iter().filter(|v| v.is_written && v.is_condition_only)
|
||||
}
|
||||
|
||||
/// Check invariant: no variable appears in multiple categories.
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
pub fn verify_invariants(&self) -> Result<(), String> {
|
||||
let mut all_names: BTreeSet<&str> = BTreeSet::new();
|
||||
|
||||
for v in &self.owned_vars {
|
||||
if !all_names.insert(&v.name) {
|
||||
return Err(format!("Duplicate owned var: {}", v.name));
|
||||
}
|
||||
}
|
||||
|
||||
for v in &self.relay_writes {
|
||||
if self.owned_vars.iter().any(|o| o.name == v.name) {
|
||||
return Err(format!("Relay var '{}' conflicts with owned", v.name));
|
||||
}
|
||||
}
|
||||
|
||||
for v in &self.captures {
|
||||
if self.owned_vars.iter().any(|o| o.name == v.name) {
|
||||
return Err(format!("Captured var '{}' conflicts with owned", v.name));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_empty_plan() {
|
||||
let plan = OwnershipPlan::new(ScopeId(1));
|
||||
assert_eq!(plan.scope_id.0, 1);
|
||||
assert!(plan.owned_vars.is_empty());
|
||||
assert_eq!(plan.carriers().count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_carriers_filter() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "sum".to_string(),
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "limit".to_string(),
|
||||
is_written: false, // read-only owned
|
||||
is_condition_only: false,
|
||||
});
|
||||
|
||||
let carriers: Vec<_> = plan.carriers().collect();
|
||||
assert_eq!(carriers.len(), 1);
|
||||
assert_eq!(carriers[0].name, "sum");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invariant_verification() {
|
||||
let mut plan = OwnershipPlan::new(ScopeId(1));
|
||||
plan.owned_vars.push(ScopeOwnedVar {
|
||||
name: "x".to_string(),
|
||||
is_written: true,
|
||||
is_condition_only: false,
|
||||
});
|
||||
|
||||
// Valid plan
|
||||
assert!(plan.verify_invariants().is_ok());
|
||||
|
||||
// Add conflicting relay
|
||||
plan.relay_writes.push(RelayVar {
|
||||
name: "x".to_string(),
|
||||
owner_scope: ScopeId(0),
|
||||
relay_path: vec![],
|
||||
});
|
||||
|
||||
// Now invalid
|
||||
assert!(plan.verify_invariants().is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user