Skip to main content

gruel_cfg/
drop_names.rs

1//! Shared drop-glue utilities: reachability checks and synthesized symbol names.
2//!
3//! Both the drop-glue synthesizer (`gruel-compiler`) and the LLVM codegen
4//! (`gruel-codegen-llvm`) need to agree on:
5//! - whether a type requires a destructor call, and
6//! - the exact name of the synthesized `__gruel_drop_*` symbol for that type.
7//!
8//! Putting these here avoids duplicating the logic and keeps the two sites in
9//! sync automatically — if the names ever need to change, there is one place to
10//! edit.
11
12use gruel_air::{Type, TypeInternPool, TypeKind};
13
14/// Return `true` if `ty` requires a drop call when it goes out of scope.
15///
16/// Primitive scalars, unit-only enums, pointers, and unit/never are trivially droppable.
17/// A struct needs drop if it has a destructor or if any field needs drop.
18/// An array needs drop if its element type needs drop.
19/// A data enum needs drop if any variant has a field that needs drop.
20pub fn type_needs_drop(ty: Type, type_pool: &TypeInternPool) -> bool {
21    match ty.kind() {
22        TypeKind::Struct(id) => {
23            let def = type_pool.struct_def(id);
24            if def.destructor.is_some() {
25                return true;
26            }
27            def.fields.iter().any(|f| type_needs_drop(f.ty, type_pool))
28        }
29        TypeKind::Array(id) => {
30            let (elem, _) = type_pool.array_def(id);
31            type_needs_drop(elem, type_pool)
32        }
33        TypeKind::Enum(id) => {
34            let def = type_pool.enum_def(id);
35            if def.destructor.is_some() {
36                return true;
37            }
38            def.variants
39                .iter()
40                .any(|v| v.fields.iter().any(|f| type_needs_drop(*f, type_pool)))
41        }
42        // Vec(T) (ADR-0066) always needs drop — owns heap memory.
43        TypeKind::Vec(_) => true,
44        _ => false,
45    }
46}
47
48/// Return the name of the synthesized `__gruel_drop_*` function for `ty`,
49/// or `None` if the type is trivially droppable.
50///
51/// ADR-0081: previously, built-in types with `destructor` set (only
52/// `String`, dropped via the runtime `__gruel_drop_String`) returned
53/// `None` here so codegen could route to the runtime symbol directly.
54/// That carve-out retired with the runtime collapse — `String` is now
55/// a regular prelude struct whose drop is auto-synthesized from its
56/// `bytes: Vec(u8)` field.
57pub fn drop_fn_name(ty: Type, type_pool: &TypeInternPool) -> Option<String> {
58    match ty.kind() {
59        TypeKind::Struct(id) => {
60            let def = type_pool.struct_def(id);
61            if type_needs_drop(ty, type_pool) {
62                Some(format!("__gruel_drop_{}", def.name))
63            } else {
64                None
65            }
66        }
67        TypeKind::Array(id) => {
68            if type_needs_drop(ty, type_pool) {
69                let (elem, len) = type_pool.array_def(id);
70                Some(format!(
71                    "__gruel_drop_array_{}_{}",
72                    type_name_component(elem, type_pool),
73                    len
74                ))
75            } else {
76                None
77            }
78        }
79        TypeKind::Enum(id) => {
80            if type_needs_drop(ty, type_pool) {
81                let def = type_pool.enum_def(id);
82                Some(format!("__gruel_drop_{}", def.name))
83            } else {
84                None
85            }
86        }
87        _ => None,
88    }
89}
90
91/// A stable, human-readable name component for `ty`, used inside drop-glue
92/// symbol names (e.g. the `String_3` in `__gruel_drop_array_String_3`).
93pub fn type_name_component(ty: Type, type_pool: &TypeInternPool) -> String {
94    match ty.kind() {
95        TypeKind::I8 => "i8".to_owned(),
96        TypeKind::I16 => "i16".to_owned(),
97        TypeKind::I32 => "i32".to_owned(),
98        TypeKind::I64 => "i64".to_owned(),
99        TypeKind::U8 => "u8".to_owned(),
100        TypeKind::U16 => "u16".to_owned(),
101        TypeKind::U32 => "u32".to_owned(),
102        TypeKind::U64 => "u64".to_owned(),
103        TypeKind::Isize => "isize".to_owned(),
104        TypeKind::Usize => "usize".to_owned(),
105        TypeKind::F16 => "f16".to_owned(),
106        TypeKind::F32 => "f32".to_owned(),
107        TypeKind::F64 => "f64".to_owned(),
108        TypeKind::Bool => "bool".to_owned(),
109        TypeKind::Char => "char".to_owned(),
110        TypeKind::Unit => "unit".to_owned(),
111        TypeKind::Never => "never".to_owned(),
112        TypeKind::Error => "error".to_owned(),
113        TypeKind::ComptimeType => "comptime_type".to_owned(),
114        TypeKind::ComptimeStr => "comptime_str".to_owned(),
115        TypeKind::ComptimeInt => "comptime_int".to_owned(),
116        // ADR-0086 C named primitive types.
117        TypeKind::CSchar => "c_schar".to_owned(),
118        TypeKind::CShort => "c_short".to_owned(),
119        TypeKind::CInt => "c_int".to_owned(),
120        TypeKind::CLong => "c_long".to_owned(),
121        TypeKind::CLonglong => "c_longlong".to_owned(),
122        TypeKind::CUchar => "c_uchar".to_owned(),
123        TypeKind::CUshort => "c_ushort".to_owned(),
124        TypeKind::CUint => "c_uint".to_owned(),
125        TypeKind::CUlong => "c_ulong".to_owned(),
126        TypeKind::CUlonglong => "c_ulonglong".to_owned(),
127        TypeKind::CFloat => "c_float".to_owned(),
128        TypeKind::CDouble => "c_double".to_owned(),
129        TypeKind::CVoid => "c_void".to_owned(),
130        TypeKind::Enum(id) => format!("enum{}", id.0),
131        TypeKind::Struct(id) => type_pool.struct_def(id).name.clone(),
132        TypeKind::Array(id) => {
133            let (elem, len) = type_pool.array_def(id);
134            format!("array_{}_{}", type_name_component(elem, type_pool), len)
135        }
136        TypeKind::Module(id) => format!("module{}", id.0),
137        TypeKind::PtrConst(id) => {
138            let pointee = type_pool.ptr_const_def(id);
139            format!("ptr_const_{}", type_name_component(pointee, type_pool))
140        }
141        TypeKind::PtrMut(id) => {
142            let pointee = type_pool.ptr_mut_def(id);
143            format!("ptr_mut_{}", type_name_component(pointee, type_pool))
144        }
145        TypeKind::Ref(id) => {
146            let referent = type_pool.ref_def(id);
147            format!("ref_{}", type_name_component(referent, type_pool))
148        }
149        TypeKind::MutRef(id) => {
150            let referent = type_pool.mut_ref_def(id);
151            format!("mut_ref_{}", type_name_component(referent, type_pool))
152        }
153        TypeKind::Slice(id) => {
154            let elem = type_pool.slice_def(id);
155            format!("slice_{}", type_name_component(elem, type_pool))
156        }
157        TypeKind::MutSlice(id) => {
158            let elem = type_pool.mut_slice_def(id);
159            format!("mut_slice_{}", type_name_component(elem, type_pool))
160        }
161        TypeKind::Vec(id) => {
162            let elem = type_pool.vec_def(id);
163            format!("vec_{}", type_name_component(elem, type_pool))
164        }
165        TypeKind::Interface(id) => format!("interface{}", id.0),
166    }
167}