use crate::analyzer::OwnershipMode;
use crate::parser::ast::operators::UnaryOp;
use crate::parser::{Expression, Type};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct OwnershipTracker {
variables: HashMap<String, OwnershipMode>,
struct_fields: HashMap<String, Type>,
copy_types: std::collections::HashSet<String>,
}
impl OwnershipTracker {
pub fn new() -> Self {
Self {
variables: HashMap::new(),
struct_fields: HashMap::new(),
copy_types: std::collections::HashSet::new(),
}
}
pub fn register_struct_field(&mut self, field_path: &str, type_: Type) {
self.struct_fields.insert(field_path.to_string(), type_);
}
pub fn register_copy_type(&mut self, name: &str) {
self.copy_types.insert(name.to_string());
if let Some(base) = name.split("::").last() {
if base != name {
self.copy_types.insert(base.to_string());
}
}
}
pub fn set_copy_types_registry(&mut self, names: &std::collections::HashSet<String>) {
for name in names {
self.register_copy_type(name);
}
}
pub fn register_parameter(&mut self, name: &str, ownership: OwnershipMode) {
self.variables.insert(name.to_string(), ownership);
}
pub fn register_binding(&mut self, name: &str, ownership: OwnershipMode) {
self.variables.insert(name.to_string(), ownership);
}
pub fn unregister_binding(&mut self, name: &str) {
self.variables.remove(name);
}
pub fn clear_function_scope(&mut self) {
self.variables.clear();
}
pub fn get_variable_ownership(&self, name: &str) -> Option<OwnershipMode> {
self.variables.get(name).copied()
}
pub fn is_borrowed_parameter(&self, name: &str) -> bool {
self.get_variable_ownership(name) == Some(OwnershipMode::Borrowed)
}
pub fn is_mut_borrowed_parameter(&self, name: &str) -> bool {
self.get_variable_ownership(name) == Some(OwnershipMode::MutBorrowed)
}
pub fn get_binding_ownership(&self, name: &str) -> Option<OwnershipMode> {
self.get_variable_ownership(name)
}
pub fn is_variable_borrowed(&self, name: &str) -> bool {
self.get_variable_ownership(name)
.is_some_and(|o| matches!(o, OwnershipMode::Borrowed | OwnershipMode::MutBorrowed))
}
pub fn ownership_from_type(ty: &Type) -> OwnershipMode {
match ty {
Type::Reference(_) => OwnershipMode::Borrowed,
Type::MutableReference(_) => OwnershipMode::MutBorrowed,
_ => OwnershipMode::Owned,
}
}
pub fn get_expression_ownership(&self, expr: &Expression) -> OwnershipMode {
match expr {
Expression::Identifier { name, .. } => self
.get_variable_ownership(name)
.unwrap_or(OwnershipMode::Owned),
Expression::FieldAccess { object, .. } => self.get_expression_ownership(object),
Expression::Index { object, .. } => self.get_expression_ownership(object),
Expression::MethodCall { object, method, .. } => {
self.get_method_call_ownership(object, method)
}
Expression::Unary { op, operand, .. } => self.get_unary_ownership(*op, operand),
Expression::Cast { expr, .. } => self.get_expression_ownership(expr),
Expression::TryOp { expr, .. } => self.get_expression_ownership(expr),
Expression::Block { statements, .. } => {
if let Some(crate::parser::Statement::Expression { expr, .. }) = statements.last() {
return self.get_expression_ownership(expr);
}
OwnershipMode::Owned
}
Expression::Literal { .. }
| Expression::Binary { .. }
| Expression::Call { .. }
| Expression::StructLiteral { .. }
| Expression::MapLiteral { .. }
| Expression::Array { .. }
| Expression::Tuple { .. }
| Expression::Range { .. }
| Expression::Closure { .. }
| Expression::MacroInvocation { .. }
| Expression::Await { .. }
| Expression::ChannelSend { .. }
| Expression::ChannelRecv { .. } => OwnershipMode::Owned,
}
}
fn get_method_call_ownership(&self, object: &Expression, method: &str) -> OwnershipMode {
if crate::type_classification::is_ownership_producing_method(method) {
OwnershipMode::Owned
} else {
self.get_expression_ownership(object)
}
}
fn get_unary_ownership(&self, op: UnaryOp, _operand: &Expression) -> OwnershipMode {
match op {
UnaryOp::Deref => OwnershipMode::Owned,
UnaryOp::Ref => OwnershipMode::Borrowed,
UnaryOp::MutRef => OwnershipMode::MutBorrowed,
UnaryOp::Not | UnaryOp::Neg => OwnershipMode::Owned,
}
}
pub fn is_copy_type(&self, ty: &Type) -> bool {
match ty {
Type::Custom(name) => {
self.copy_types.contains(name.as_str())
|| name
.split("::")
.last()
.is_some_and(|b| self.copy_types.contains(b))
}
_ => false,
}
}
pub fn ownership_from_param_type(param_type: &Type) -> OwnershipMode {
match param_type {
Type::Reference(_) => OwnershipMode::Borrowed,
Type::MutableReference(_) => OwnershipMode::MutBorrowed,
_ => OwnershipMode::Owned,
}
}
}
impl Default for OwnershipTracker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_and_lookup_parameter() {
let mut tracker = OwnershipTracker::new();
tracker.register_parameter("data", OwnershipMode::Borrowed);
assert_eq!(
tracker.get_variable_ownership("data"),
Some(OwnershipMode::Borrowed)
);
}
#[test]
fn test_register_binding() {
let mut tracker = OwnershipTracker::new();
tracker.register_binding("item", OwnershipMode::Borrowed);
assert_eq!(
tracker.get_variable_ownership("item"),
Some(OwnershipMode::Borrowed)
);
}
#[test]
fn test_clear_function_scope() {
let mut tracker = OwnershipTracker::new();
tracker.register_parameter("x", OwnershipMode::Owned);
tracker.clear_function_scope();
assert_eq!(tracker.get_variable_ownership("x"), None);
}
#[test]
fn test_ownership_from_param_type() {
assert_eq!(
OwnershipTracker::ownership_from_param_type(&Type::Reference(Box::new(Type::Int))),
OwnershipMode::Borrowed
);
assert_eq!(
OwnershipTracker::ownership_from_param_type(&Type::MutableReference(Box::new(
Type::Int
))),
OwnershipMode::MutBorrowed
);
assert_eq!(
OwnershipTracker::ownership_from_param_type(&Type::Int),
OwnershipMode::Owned
);
}
#[test]
fn test_register_copy_type() {
let mut tracker = OwnershipTracker::new();
tracker.register_copy_type("Vec3");
assert!(tracker.is_copy_type(&Type::Custom("Vec3".to_string())));
assert!(tracker.is_copy_type(&Type::Custom("math::Vec3".to_string())));
}
}