refactor(joinir): phase 100 P1 - add CapturedKind (Explicit/Pinned) to CapturedVar

- Add CapturedKind enum to types.rs (Explicit for traditional, Pinned for Phase 100)
- Update CapturedVar struct to include kind field
- Add insert() and insert_pinned() helper methods to CapturedEnv
- Update analyzers.rs to specify kind field in all CapturedVar instantiations
- Export CapturedKind from function_scope_capture module
- Update scope_manager.rs test to include kind field

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-17 05:32:17 +09:00
parent 692d44da62
commit 0c7ea21cac
4 changed files with 65 additions and 3 deletions

View File

@ -465,6 +465,7 @@ mod tests {
name: "len".to_string(),
host_id: ValueId(42),
is_immutable: true,
kind: crate::mir::loop_pattern_detection::function_scope_capture::CapturedKind::Explicit,
});
let carrier_info = CarrierInfo {

View File

@ -6,7 +6,7 @@ use crate::mir::ValueId;
use std::collections::BTreeSet;
use super::helpers::*;
use super::types::{CapturedEnv, CapturedVar};
use super::types::{CapturedEnv, CapturedKind, CapturedVar};
/// Analyzes function-scoped variables that can be safely captured for loop conditions/body.
///
@ -159,6 +159,7 @@ pub(crate) fn analyze_captured_vars(
name: name.clone(),
host_id: ValueId(0), // Placeholder, will be resolved in ConditionEnvBuilder
is_immutable: true,
kind: CapturedKind::Explicit,
});
}
@ -296,6 +297,7 @@ pub(crate) fn analyze_captured_vars_v2(
name: name.clone(),
host_id: ValueId(0), // Placeholder, will be resolved in ConditionEnvBuilder
is_immutable: true,
kind: CapturedKind::Explicit,
});
}
@ -343,6 +345,7 @@ pub(crate) fn analyze_captured_vars_v2(
name: name.clone(),
host_id: ValueId(0), // Placeholder, will be resolved in ConditionEnvBuilder
is_immutable: true,
kind: CapturedKind::Explicit,
});
}

View File

@ -54,4 +54,4 @@ mod types;
// Public re-exports
pub(crate) use analyzers::analyze_captured_vars_v2;
pub use types::{CapturedEnv, CapturedVar};
pub use types::{CapturedEnv, CapturedKind, CapturedVar};

View File

@ -2,6 +2,18 @@
use crate::mir::ValueId;
/// Classification of captured variable (Phase 100 expansion)
///
/// - `Explicit`: Captured for condition/carrier usage (traditional, Phase 200-A/B)
/// - `Pinned`: Read-only local from loop-outer scope, used as receiver in loop body (Phase 100)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CapturedKind {
/// Traditional capture (condition variables, carriers)
Explicit,
/// Phase 100: read-only local (dynamic construction allowed, immutable in loop)
Pinned,
}
/// A variable captured from function scope for use in loop conditions/body.
///
/// Example: `local digits = "0123456789"` in JsonParser._atoi()
@ -11,9 +23,10 @@ use crate::mir::ValueId;
/// - `name`: Variable name as it appears in the source code
/// - `host_id`: MIR ValueId of the original definition in the host function
/// - `is_immutable`: True if the variable is never reassigned in the function
/// - `kind`: Classification as Explicit (traditional) or Pinned (Phase 100 read-only)
#[derive(Debug, Clone)]
pub struct CapturedVar {
/// Variable name (e.g., "digits", "table")
/// Variable name (e.g., "digits", "table", "s")
pub name: String,
/// MIR ValueId of the original definition in the host function
@ -24,6 +37,9 @@ pub struct CapturedVar {
/// Phase 200-B will implement assignment analysis to determine this.
/// For now, this is always set to true as a conservative default.
pub is_immutable: bool,
/// Phase 100: Classification of captured variable
pub kind: CapturedKind,
}
/// Environment containing function-scoped captured variables.
@ -53,6 +69,26 @@ impl CapturedEnv {
self.vars.push(var);
}
/// Add an explicit captured variable (traditional capture)
pub fn insert(&mut self, name: String, host_id: ValueId) {
self.add_var(CapturedVar {
name,
host_id,
is_immutable: true,
kind: CapturedKind::Explicit,
});
}
/// Add a pinned captured variable (Phase 100: read-only local)
pub fn insert_pinned(&mut self, name: String, host_id: ValueId) {
self.add_var(CapturedVar {
name,
host_id,
is_immutable: true,
kind: CapturedKind::Pinned,
});
}
/// Look up a captured variable by name
///
/// Returns `Some(&CapturedVar)` if found, `None` otherwise.
@ -79,6 +115,7 @@ mod tests {
name: "digits".to_string(),
host_id: ValueId(42),
is_immutable: true,
kind: CapturedKind::Explicit,
});
assert!(!env.is_empty());
@ -86,6 +123,7 @@ mod tests {
assert_eq!(var.name, "digits");
assert_eq!(var.host_id, ValueId(42));
assert!(var.is_immutable);
assert_eq!(var.kind, CapturedKind::Explicit);
}
#[test]
@ -95,11 +133,13 @@ mod tests {
name: "digits".to_string(),
host_id: ValueId(42),
is_immutable: true,
kind: CapturedKind::Explicit,
});
env.add_var(CapturedVar {
name: "table".to_string(),
host_id: ValueId(100),
is_immutable: true,
kind: CapturedKind::Pinned,
});
assert_eq!(env.vars.len(), 2);
@ -107,4 +147,22 @@ mod tests {
assert!(env.get("table").is_some());
assert!(env.get("nonexistent").is_none());
}
#[test]
fn test_captured_env_insert_explicit() {
let mut env = CapturedEnv::new();
env.insert("x".to_string(), ValueId(10));
let var = env.get("x").unwrap();
assert_eq!(var.kind, CapturedKind::Explicit);
}
#[test]
fn test_captured_env_insert_pinned() {
let mut env = CapturedEnv::new();
env.insert_pinned("s".to_string(), ValueId(20));
let var = env.get("s").unwrap();
assert_eq!(var.kind, CapturedKind::Pinned);
}
}