1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
//! Copy Semantics Layer - Applies Rust's Copy rules to ownership tracking
//!
//! Key insight: In Rust, Copy types behave differently from non-Copy types:
//! - `let x = ©_value` → x is &T, but using x auto-copies to T
//! - `let (a, b) = &(1, 2)` → a and b are i32 (owned), not &i32
//! - `struct.copy_field` from &struct → owned T, not &T
//!
//! This layer converts tracked ownership (Borrowed/MutBorrowed/Owned) to
//! EFFECTIVE ownership based on whether the type implements Copy.
//! Also encodes when Rust applies automatic dereferencing or copying,
//! used by rust_coercion_rules to determine required coercions.
use crate::analyzer::OwnershipMode;
use crate::parser::Type;
use std::collections::HashSet;
pub struct CopySemantics {
copy_types: HashSet<String>,
}
impl CopySemantics {
pub fn new() -> Self {
Self {
copy_types: HashSet::new(),
}
}
/// Register a Copy type from @derive(Copy) registry
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());
}
}
}
/// Set the entire Copy registry
pub fn set_copy_types(&mut self, types: &HashSet<String>) {
for ty in types {
self.register_copy_type(ty);
}
}
/// Get the type to check for Copy semantics.
/// For references (&T, &mut T), we check the POINTEE (T) - when we use a reference
/// in a binary op, Rust auto-derefs to the inner type. So &i32 in `a + b` uses i32's Copy.
fn type_for_copy_check(ty: &Type) -> &Type {
match ty {
Type::Reference(inner) | Type::MutableReference(inner) => inner,
_ => ty,
}
}
/// Check if a type implements Copy
pub fn is_type_copy(&self, ty: &Type) -> bool {
let ty = Self::type_for_copy_check(ty);
match ty {
// Primitives are always Copy (parser Type variants)
Type::Int | Type::Int32 | Type::Uint | Type::Float | Type::Bool => true,
// Custom types: check registry or known primitives
Type::Custom(name) => {
self.copy_types.contains(name.as_str())
|| name
.split("::")
.last()
.is_some_and(|b| self.copy_types.contains(b))
|| crate::type_classification::is_copy_primitive(name)
|| name == "int"
}
// Tuples are Copy if all elements are Copy
Type::Tuple(types) => types.iter().all(|t| self.is_type_copy(t)),
// Option<T> is Copy if T is Copy
Type::Option(inner) => self.is_type_copy(inner),
// Array[T; N] is Copy if T is Copy
Type::Array(inner, _) => self.is_type_copy(inner),
// References (after stripping): Vec, Result, etc. are not Copy
_ => false,
}
}
/// Get EFFECTIVE ownership after applying Copy semantics
///
/// Key insight: &Copy in Rust auto-copies, yielding owned values
pub fn effective_ownership(
&self,
tracked_ownership: OwnershipMode,
expr_type: &Type,
) -> OwnershipMode {
match tracked_ownership {
// &Copy or &mut Copy → Owned (Rust auto-copies)
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed
if self.is_type_copy(expr_type) =>
{
OwnershipMode::Owned // Copy gives you owned value!
}
// &Non-Copy → still Borrowed (can't auto-copy)
// Owned → still Owned
other => other,
}
}
/// Check if expression ACTUALLY needs explicit deref in generated Rust
///
/// Rust auto-derefs in many contexts, we should NOT add * there
pub fn needs_explicit_deref(
&self,
context: DerefContext,
ownership: OwnershipMode,
expr_type: &Type,
) -> bool {
let is_copy = self.is_type_copy(expr_type);
match (ownership, is_copy, context) {
// Copy types: Rust auto-copies in most contexts
(
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed,
true,
DerefContext::Comparison,
) => false,
(
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed,
true,
DerefContext::MethodCall,
) => false,
(
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed,
true,
DerefContext::FieldAccess,
) => false,
(
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed,
true,
DerefContext::BinaryOp,
) => false,
(
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed,
true,
DerefContext::StructLiteral,
) => false,
(
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed,
true,
DerefContext::FunctionArg,
) => false,
(
OwnershipMode::Borrowed | OwnershipMode::MutBorrowed,
true,
DerefContext::Standalone,
) => false,
// Non-Copy from borrow: need .clone(), not *
(OwnershipMode::Borrowed | OwnershipMode::MutBorrowed, false, _) => false,
// Owned: never need deref
(OwnershipMode::Owned, _, _) => false,
}
}
}
impl Default for CopySemantics {
fn default() -> Self {
Self::new()
}
}
/// Context in which an expression is used - affects Rust's auto-deref/auto-copy behavior
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DerefContext {
/// Comparison operators (==, !=, <, etc.) - Rust auto-derefs and auto-copies
Comparison,
/// Method call receiver - Rust auto-derefs
MethodCall,
/// Field access (expr.field) - Rust auto-derefs
FieldAccess,
/// Binary operators (+, -, etc.) - Rust auto-derefs and auto-copies
BinaryOp,
/// Struct literal field - Rust auto-copies for Copy types
StructLiteral,
/// Function argument - Rust auto-copies for Copy types
FunctionArg,
/// Standalone expression (assignment RHS, return, etc.) - no special coercion
Standalone,
}