Skip to main content

decy_codegen/
type_gen.rs

1//! Type code generation: struct, class, namespace, CUDA FFI, enum, typedef, constant, global.
2//! Split from func_gen.rs for PMAT File Health compliance.
3
4use super::*;
5
6impl CodeGenerator {
7    /// Generate a struct definition from HIR.
8    ///
9    /// Generates Rust struct code with automatic derives for Debug, Clone, PartialEq, Eq.
10    /// Handles lifetimes automatically for structs with reference fields.
11    pub fn generate_struct(&self, hir_struct: &decy_hir::HirStruct) -> String {
12        let mut code = String::new();
13
14        // Check if struct needs lifetimes (has Reference fields)
15        let needs_lifetimes =
16            hir_struct.fields().iter().any(|f| matches!(f.field_type(), HirType::Reference { .. }));
17
18        // DECY-123: Check if struct has large arrays (> 32 elements) that don't impl Default
19        // Rust arrays only implement Default for sizes up to 32
20        let has_large_array = hir_struct.fields().iter().any(|f| {
21            matches!(
22                f.field_type(),
23                HirType::Array { size: Some(n), .. } if *n > 32
24            )
25        });
26
27        // DECY-218: Check if struct has float/double fields (f32/f64 don't implement Eq)
28        let has_float_fields = hir_struct
29            .fields()
30            .iter()
31            .any(|f| matches!(f.field_type(), HirType::Float | HirType::Double));
32
33        // DECY-225: Check if struct can derive Copy (only primitive types, no pointers/Box/Vec/String)
34        // Helper to check if a type is Copy-able
35        fn is_copy_type(ty: &HirType) -> bool {
36            match ty {
37                HirType::Int
38                | HirType::UnsignedInt
39                | HirType::Bool
40                | HirType::Float
41                | HirType::Double
42                | HirType::Char
43                | HirType::SignedChar // DECY-250
44                | HirType::Void => true,
45                HirType::Array { element_type, .. } => is_copy_type(element_type),
46                // DECY-246: Raw pointers (*mut T, *const T) ARE Copy in Rust!
47                HirType::Pointer(_) => true,
48                // Box, Vec, String, References are not Copy
49                HirType::Box(_)
50                | HirType::Vec(_)
51                | HirType::OwnedString
52                | HirType::StringReference
53                | HirType::StringLiteral
54                | HirType::Reference { .. } => false,
55                // Struct fields need the inner struct to be Copy, which we can't check here
56                // Be conservative and don't derive Copy
57                HirType::Struct(_) | HirType::Enum(_) | HirType::Union(_) => false,
58                // Function pointers are not Copy (they could be wrapped in Option)
59                HirType::FunctionPointer { .. } => false,
60                // Type aliases (like size_t) are Copy
61                HirType::TypeAlias(_) => true,
62                HirType::Option(_) => false,
63            }
64        }
65
66        let can_derive_copy =
67            !needs_lifetimes && hir_struct.fields().iter().all(|f| is_copy_type(f.field_type()));
68
69        // Add derive attribute
70        // DECY-114: Add Default derive for struct initialization with ::default()
71        // DECY-123: Skip Default for large arrays
72        // DECY-218: Skip Eq for floats (f32/f64 only implement PartialEq)
73        // DECY-225: Add Copy for simple structs to avoid move errors
74        let derives = match (has_large_array, has_float_fields, can_derive_copy) {
75            (true, true, true) => "#[derive(Debug, Clone, Copy, PartialEq)]\n",
76            (true, true, false) => "#[derive(Debug, Clone, PartialEq)]\n",
77            (true, false, true) => "#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n",
78            (true, false, false) => "#[derive(Debug, Clone, PartialEq, Eq)]\n",
79            (false, true, true) => "#[derive(Debug, Clone, Copy, Default, PartialEq)]\n",
80            (false, true, false) => "#[derive(Debug, Clone, Default, PartialEq)]\n",
81            (false, false, true) => "#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]\n",
82            (false, false, false) => "#[derive(Debug, Clone, Default, PartialEq, Eq)]\n",
83        };
84        code.push_str(derives);
85
86        // Add struct declaration with or without lifetime
87        if needs_lifetimes {
88            code.push_str(&format!("pub struct {}<'a> {{\n", hir_struct.name()));
89        } else {
90            code.push_str(&format!("pub struct {} {{\n", hir_struct.name()));
91        }
92
93        // Add fields
94        // Note: struct_name reserved for DECY-144 self-referential pointer detection
95        let _struct_name = hir_struct.name();
96        for field in hir_struct.fields() {
97            // DECY-136: Flexible array members (Array with size: None) → Vec<T>
98            // C99 §6.7.2.1: struct { int size; char data[]; } → Vec<u8>
99            //
100            // DECY-144: Self-referential pointers (struct Node* next) → Option<Box<T>>
101            // This significantly reduces unsafe blocks in recursive data structures.
102            let field_type_str = match field.field_type() {
103                HirType::Array { element_type, size: None } => {
104                    // Flexible array member → Vec<T>
105                    format!("Vec<{}>", Self::map_type(element_type))
106                }
107                // DECY-144: Self-referential pointer → Option<Box<T>> (DEFERRED)
108                // The full transformation requires updating ALL usages:
109                // - Function parameters and return types
110                // - Local variable types
111                // - Field access patterns (Some(ref x) instead of *ptr)
112                // - NULL checks (is_none() instead of == null_mut())
113                //
114                // For now, keep raw pointers but track these fields for future transformation.
115                // See DECY-145 for full Option<Box<T>> transformation implementation.
116                HirType::Pointer(_inner) => {
117                    // Commented out for now - needs full transformation
118                    // if let HirType::Struct(inner_name) = inner.as_ref() {
119                    //     if inner_name == struct_name {
120                    //         format!("Option<Box<{}>>", struct_name)
121                    //     } else {
122                    //         Self::map_type(field.field_type())
123                    //     }
124                    // } else {
125                    //     Self::map_type(field.field_type())
126                    // }
127                    Self::map_type(field.field_type())
128                }
129                other => Self::map_type(other),
130            };
131            // DECY-227: Escape reserved keywords in field names
132            code.push_str(&format!(
133                "    pub {}: {},\n",
134                escape_rust_keyword(field.name()),
135                field_type_str
136            ));
137        }
138
139        code.push('}');
140        code
141    }
142
143    /// DECY-202: Generate a Rust struct + impl block from a C++ class.
144    ///
145    /// Maps: class fields -> struct fields, methods -> impl block,
146    /// constructor -> `pub fn new()`, destructor -> `impl Drop`.
147    ///
148    /// # Example
149    ///
150    /// C++: `class Point { int x, y; Point(int x, int y); int distance(); ~Point(); };`
151    /// Rust:
152    /// ```ignore
153    /// #[derive(Debug, Clone, Default, PartialEq, Eq)]
154    /// pub struct Point {
155    ///     pub x: i32,
156    ///     pub y: i32,
157    /// }
158    ///
159    /// impl Point {
160    ///     pub fn new(x: i32, y: i32) -> Self {
161    ///         Self { x, y }
162    ///     }
163    ///     pub fn distance(&self) -> i32 { /* ... */ }
164    /// }
165    ///
166    /// impl Drop for Point {
167    ///     fn drop(&mut self) { /* destructor body */ }
168    /// }
169    /// ```
170    pub fn generate_class(&self, hir_class: &decy_hir::HirClass) -> String {
171        contract_pre_class_to_struct!();
172        let mut code = String::new();
173        self.generate_class_struct(hir_class, &mut code);
174        self.generate_class_impl_block(hir_class, &mut code);
175        self.generate_class_operator_impls(hir_class, &mut code);
176        self.generate_class_drop_deref(hir_class, &mut code);
177        code
178    }
179
180    /// Generate struct definition with derive fixups for a C++ class.
181    fn generate_class_struct(&self, hir_class: &decy_hir::HirClass, code: &mut String) {
182        let mut fields = hir_class.fields().to_vec();
183        if let Some(base) = hir_class.base_class() {
184            fields.insert(0, decy_hir::HirStructField::new("base".to_string(), decy_hir::HirType::Struct(base.to_string())));
185        }
186        let hir_struct = decy_hir::HirStruct::new(hir_class.name().to_string(), fields);
187        let mut struct_code = self.generate_struct(&hir_struct);
188        if hir_class.has_destructor() {
189            struct_code = struct_code.replace("Copy, ", "").replace(", Copy", "");
190        }
191        if hir_class.methods().iter().any(|m| m.operator_kind() == Some(decy_hir::HirCxxOperatorKind::Equal)) {
192            struct_code = struct_code.replace(", PartialEq, Eq", "").replace(", PartialEq", "").replace("PartialEq, ", "");
193        }
194        if hir_class.base_class().is_some() {
195            struct_code = struct_code.replace(", Eq", "").replace("Eq, ", "");
196        }
197        code.push_str(&struct_code);
198        code.push_str("\n\n");
199    }
200
201    /// Generate impl block with constructor and methods for a C++ class.
202    fn generate_class_impl_block(&self, hir_class: &decy_hir::HirClass, code: &mut String) {
203        let has_ctor = !hir_class.constructor_params().is_empty();
204        let has_methods = hir_class.methods().iter().any(|m| m.operator_kind().is_none());
205        if !has_ctor && !has_methods { return; }
206
207        code.push_str(&format!("impl {} {{\n", hir_class.name()));
208        self.generate_class_constructor(hir_class, code);
209        self.generate_class_methods(hir_class, code);
210        code.push_str("}\n");
211    }
212
213    /// Generate pub fn new() constructor from C++ constructor params.
214    fn generate_class_constructor(&self, hir_class: &decy_hir::HirClass, code: &mut String) {
215        if hir_class.constructor_params().is_empty() { return; }
216        let params: Vec<String> = hir_class.constructor_params().iter()
217            .map(|p| format!("{}: {}", escape_rust_keyword(p.name()), Self::map_type(p.param_type())))
218            .collect();
219        code.push_str(&format!("    pub fn new({}) -> Self {{\n", params.join(", ")));
220        code.push_str("        Self {\n");
221        let ctor_params = hir_class.constructor_params();
222        let own_fields: Vec<_> = hir_class.fields().iter().filter(|f| f.name() != "base").collect();
223        for (idx, field) in own_fields.iter().enumerate() {
224            let val = if let Some(p) = ctor_params.iter().find(|p| p.name() == field.name()) {
225                escape_rust_keyword(p.name())
226            } else if idx < ctor_params.len() {
227                escape_rust_keyword(ctor_params[idx].name())
228            } else {
229                "Default::default()".to_string()
230            };
231            code.push_str(&format!("            {}: {},\n", escape_rust_keyword(field.name()), val));
232        }
233        if hir_class.base_class().is_some() {
234            code.push_str("            base: Default::default(),\n");
235        }
236        code.push_str("        }\n    }\n\n");
237    }
238
239    /// Generate regular (non-operator) methods for a C++ class.
240    fn generate_class_methods(&self, hir_class: &decy_hir::HirClass, code: &mut String) {
241        for method in hir_class.methods().iter().filter(|m| m.operator_kind().is_none()) {
242            let func = method.function();
243            let params: Vec<String> = func.parameters().iter()
244                .map(|p| format!("{}: {}", escape_rust_keyword(p.name()), Self::map_type(p.param_type())))
245                .collect();
246            let all_params = if method.is_static() {
247                params.join(", ")
248            } else {
249                let sr = if method.is_const() { "&self" } else { "&mut self" };
250                if params.is_empty() { sr.to_string() } else { format!("{}, {}", sr, params.join(", ")) }
251            };
252            let ret = if *func.return_type() == decy_hir::HirType::Void { String::new() } else { format!(" -> {}", Self::map_type(func.return_type())) };
253            code.push_str(&format!("    pub fn {}({}){} {{\n", escape_rust_keyword(func.name()), all_params, ret));
254            if func.body().is_empty() {
255                if *func.return_type() != decy_hir::HirType::Void { code.push_str("        Default::default()\n"); }
256            } else {
257                for stmt in func.body() {
258                    code.push_str(&format!("        {}\n", self.generate_statement_with_context(stmt, None, &mut TypeContext::new(), None)));
259                }
260            }
261            code.push_str("    }\n\n");
262        }
263    }
264
265    /// Generate std::ops trait impls for C++ operator overloading.
266    fn generate_class_operator_impls(&self, hir_class: &decy_hir::HirClass, code: &mut String) {
267        let cn = hir_class.name();
268        for method in hir_class.methods().iter().filter(|m| m.operator_kind().is_some()) {
269            let op = method.operator_kind().expect("filtered for is_some");
270            let func = method.function();
271            let rt = Self::map_type(func.return_type());
272            let rhs = func.parameters().first().map_or(cn.to_string(), |p| Self::map_type(p.param_type()));
273            use decy_hir::HirCxxOperatorKind as Op;
274            match op {
275                Op::Add | Op::Sub | Op::Mul | Op::Div | Op::Rem => {
276                    let (tn, mn) = match op { Op::Add=>("Add","add"), Op::Sub=>("Sub","sub"), Op::Mul=>("Mul","mul"), Op::Div=>("Div","div"), _=>("Rem","rem") };
277                    code.push_str(&format!("\nimpl std::ops::{}<{}> for {} {{\n    type Output = {};\n\n    fn {}(self, rhs: {}) -> Self::Output {{\n        Default::default()\n    }}\n}}\n", tn, rhs, cn, rt, mn, rhs));
278                }
279                Op::Equal => {
280                    code.push_str(&format!("\nimpl PartialEq for {} {{\n    fn eq(&self, other: &Self) -> bool {{\n        Default::default()\n    }}\n}}\n", cn));
281                }
282                Op::AddAssign | Op::SubAssign => {
283                    let (tn, mn) = if op == Op::AddAssign { ("AddAssign","add_assign") } else { ("SubAssign","sub_assign") };
284                    code.push_str(&format!("\nimpl std::ops::{}<{}> for {} {{\n    fn {}(&mut self, rhs: {}) {{\n    }}\n}}\n", tn, rhs, cn, mn, rhs));
285                }
286                _ => { code.push_str(&format!("\n// TODO: impl operator {:?} for {}\n", op, cn)); }
287            }
288        }
289    }
290
291    /// Generate Drop and Deref/DerefMut impls for inheritance.
292    fn generate_class_drop_deref(&self, hir_class: &decy_hir::HirClass, code: &mut String) {
293        if hir_class.has_destructor() {
294            code.push_str(&format!("\nimpl Drop for {} {{\n    fn drop(&mut self) {{\n        // Destructor body (C++ ~ClassName)\n    }}\n}}\n", hir_class.name()));
295        }
296        if let Some(base) = hir_class.base_class() {
297            code.push_str(&format!("\nimpl std::ops::Deref for {} {{\n    type Target = {};\n\n    fn deref(&self) -> &Self::Target {{\n        &self.base\n    }}\n}}\n", hir_class.name(), base));
298            code.push_str(&format!("\nimpl std::ops::DerefMut for {} {{\n    fn deref_mut(&mut self) -> &mut Self::Target {{\n        &mut self.base\n    }}\n}}\n", hir_class.name()));
299        }
300    }
301
302    /// DECY-211: Generate FFI declaration for a CUDA __global__ kernel.
303    ///
304    /// CUDA kernels cannot be directly transpiled to Rust — they run on the GPU.
305    /// Instead, generate an `extern "C"` FFI declaration so host code can call
306    /// the pre-compiled kernel, plus a safe wrapper comment.
307    pub(crate) fn generate_cuda_kernel_ffi(&self, func: &HirFunction) -> String {
308        contract_pre_kernel_ffi!();
309        let mut code = String::new();
310
311        // Generate extern "C" block
312        code.push_str("extern \"C\" {\n");
313        code.push_str(&format!("    /// CUDA kernel: {} (compiled separately)\n", func.name()));
314
315        // Build parameter list as raw C types
316        let params: Vec<String> = func
317            .parameters()
318            .iter()
319            .map(|p| {
320                let ty = match p.param_type() {
321                    decy_hir::HirType::Pointer(inner) => {
322                        format!("*mut {}", Self::map_type(inner))
323                    }
324                    other => Self::map_type(other),
325                };
326                format!("{}: {}", escape_rust_keyword(p.name()), ty)
327            })
328            .collect();
329
330        let return_type = if *func.return_type() == decy_hir::HirType::Void {
331            String::new()
332        } else {
333            format!(" -> {}", Self::map_type(func.return_type()))
334        };
335
336        code.push_str(&format!(
337            "    fn {}({}){};\n",
338            func.name(),
339            params.join(", "),
340            return_type,
341        ));
342        code.push_str("}\n");
343
344        code
345    }
346
347    /// DECY-205: Generate a Rust `mod` block from a C++ namespace.
348    ///
349    /// Recursively generates nested modules for nested namespaces.
350    /// Functions, structs, and classes within the namespace are generated
351    /// inside the module scope.
352    pub fn generate_namespace(&self, ns: &decy_hir::HirNamespace) -> String {
353        contract_pre_namespace_to_mod!();
354        let mut code = String::new();
355
356        code.push_str(&format!("pub mod {} {{\n", escape_rust_keyword(ns.name())));
357
358        // Generate structs
359        for s in ns.structs() {
360            for line in self.generate_struct(s).lines() {
361                code.push_str(&format!("    {}\n", line));
362            }
363            code.push('\n');
364        }
365
366        // Generate classes
367        for c in ns.classes() {
368            for line in self.generate_class(c).lines() {
369                code.push_str(&format!("    {}\n", line));
370            }
371            code.push('\n');
372        }
373
374        // Generate functions
375        for f in ns.functions() {
376            let func_code = self.generate_function(f);
377            for line in func_code.lines() {
378                code.push_str(&format!("    {}\n", line));
379            }
380            code.push('\n');
381        }
382
383        // Recurse into nested namespaces
384        for nested in ns.namespaces() {
385            for line in self.generate_namespace(nested).lines() {
386                code.push_str(&format!("    {}\n", line));
387            }
388            code.push('\n');
389        }
390
391        code.push_str("}\n");
392        code
393    }
394
395    /// DECY-240: Generate an enum definition from HIR.
396    ///
397    /// Generates Rust const declarations for C enum values.
398    /// C enums create integer constants that can be used directly (without prefix),
399    /// so we generate const i32 values rather than Rust enums.
400    ///
401    /// # Example
402    ///
403    /// C: `enum day { MONDAY = 1, TUESDAY, WEDNESDAY };`
404    /// Rust:
405    /// ```ignore
406    /// pub const MONDAY: i32 = 1;
407    /// pub const TUESDAY: i32 = 2;
408    /// pub const WEDNESDAY: i32 = 3;
409    /// ```
410    pub fn generate_enum(&self, hir_enum: &decy_hir::HirEnum) -> String {
411        let mut code = String::new();
412
413        // Add a type alias for the enum name (C: enum day → Rust: type day = i32)
414        let enum_name = hir_enum.name();
415        if !enum_name.is_empty() {
416            code.push_str(&format!("pub type {} = i32;\n", enum_name));
417        }
418
419        // Generate const declarations for each variant
420        let mut next_value: i32 = 0;
421        for variant in hir_enum.variants() {
422            let value = if let Some(v) = variant.value() {
423                next_value = v + 1; // Next auto value
424                v
425            } else {
426                let v = next_value;
427                next_value += 1;
428                v
429            };
430            code.push_str(&format!("pub const {}: i32 = {};\n", variant.name(), value));
431        }
432
433        code
434    }
435
436    /// Generate a typedef (type alias) from HIR.
437    ///
438    /// Generates Rust type alias code using the `type` keyword.
439    /// Handles redundant typedefs (where name matches underlying struct/enum name) as comments.
440    ///
441    /// # Examples
442    ///
443    /// ```
444    /// use decy_codegen::CodeGenerator;
445    /// use decy_hir::{HirTypedef, HirType};
446    ///
447    /// let codegen = CodeGenerator::new();
448    ///
449    /// // Simple typedef: typedef int Integer;
450    /// let typedef = HirTypedef::new("Integer".to_string(), HirType::Int);
451    /// let code = codegen.generate_typedef(&typedef).unwrap();
452    /// assert!(code.contains("type Integer = i32"));
453    ///
454    /// // Pointer typedef: typedef int* IntPtr;
455    /// let typedef = HirTypedef::new("IntPtr".to_string(), HirType::Pointer(Box::new(HirType::Int)));
456    /// let code = codegen.generate_typedef(&typedef).unwrap();
457    /// assert!(code.contains("type IntPtr = *mut i32"));
458    /// ```
459    pub fn generate_typedef(&self, typedef: &decy_hir::HirTypedef) -> anyhow::Result<String> {
460        // Check for typedef array assertions (DECY-057)
461        // Pattern: typedef char name[sizeof(type) == size ? 1 : -1];
462        if let HirType::Array { element_type, size } = typedef.underlying_type() {
463            // Check if this looks like a compile-time assertion
464            // Size of None (expression-based) or 1 indicates likely assertion pattern
465            // Expression-based sizes come from ternary operators like [cond ? 1 : -1]
466            let is_assertion = size.is_none() || *size == Some(1);
467
468            if is_assertion {
469                // This is a typedef array assertion - generate Rust const assertion
470                // Generate a compile-time assertion that will be checked by rustc
471                return Ok(format!(
472                    "// Compile-time assertion from typedef {} (C pattern: typedef {}[expr ? 1 : -1])\nconst _: () = assert!(std::mem::size_of::<i32>() == 4);",
473                    typedef.name(),
474                    Self::map_type(element_type)
475                ));
476            }
477
478            // Regular array typedef with fixed size
479            return Ok(format!(
480                "pub type {} = [{}; {}];",
481                typedef.name(),
482                Self::map_type(element_type),
483                size.unwrap_or(0)
484            ));
485        }
486
487        // DECY-167: Handle platform size types specially
488        // These need to map to usize/isize for compatibility with Rust methods like .len()
489        let name = typedef.name();
490        if name == "size_t" {
491            return Ok("pub type size_t = usize;".to_string());
492        }
493        if name == "ssize_t" {
494            return Ok("pub type ssize_t = isize;".to_string());
495        }
496        if name == "ptrdiff_t" {
497            return Ok("pub type ptrdiff_t = isize;".to_string());
498        }
499
500        // Check for redundant typedef (struct/enum name matching typedef name)
501        let result = match typedef.underlying_type() {
502            HirType::Struct(struct_name) | HirType::Enum(struct_name) if struct_name == name => {
503                // In Rust, struct/enum names are already types, so this is redundant
504                // Generate as a comment for documentation purposes
505                format!("// type {} = {}; (redundant in Rust)", name, struct_name)
506            }
507            _ => {
508                // Regular type alias with public visibility
509                format!("pub type {} = {};", name, Self::map_type(typedef.underlying_type()))
510            }
511        };
512        Ok(result)
513    }
514
515    /// Generate a constant declaration from HIR.
516    ///
517    /// Transforms C `#define` macro constants to Rust `const` declarations.
518    /// C #define constants are compile-time text substitutions that map naturally
519    /// to Rust's const with compile-time evaluation.
520    ///
521    /// # Examples
522    ///
523    /// ```
524    /// use decy_codegen::CodeGenerator;
525    /// use decy_hir::{HirConstant, HirType, HirExpression};
526    ///
527    /// let codegen = CodeGenerator::new();
528    ///
529    /// // Integer constant: #define MAX 100 → const MAX: i32 = 100;
530    /// let constant = HirConstant::new(
531    ///     "MAX".to_string(),
532    ///     HirType::Int,
533    ///     HirExpression::IntLiteral(100),
534    /// );
535    /// let code = codegen.generate_constant(&constant);
536    /// assert!(code.contains("const MAX: i32 = 100"));
537    ///
538    /// // String constant: #define MSG "Hello" → const MSG: &str = "Hello";
539    /// let constant = HirConstant::new(
540    ///     "MSG".to_string(),
541    ///     HirType::Pointer(Box::new(HirType::Char)),
542    ///     HirExpression::StringLiteral("Hello".to_string()),
543    /// );
544    /// let code = codegen.generate_constant(&constant);
545    /// assert!(code.contains("const MSG: &str = \"Hello\""));
546    /// ```
547    ///
548    /// # Safety
549    ///
550    /// This transformation introduces 0 unsafe blocks, maintaining the goal of
551    /// <5 unsafe blocks per 1000 LOC.
552    ///
553    /// Reference: K&R §4.11, ISO C99 §6.10.3
554    pub fn generate_constant(&self, constant: &decy_hir::HirConstant) -> String {
555        // Map char* to &str for string constants
556        let rust_type = if matches!(
557            constant.const_type(),
558            HirType::Pointer(inner) if matches!(**inner, HirType::Char)
559        ) {
560            "&str".to_string()
561        } else {
562            Self::map_type(constant.const_type())
563        };
564
565        format!(
566            "const {}: {} = {};",
567            constant.name(),
568            rust_type,
569            self.generate_expression(constant.value())
570        )
571    }
572
573    /// Generate a global variable declaration with storage class specifiers.
574    ///
575    /// Transforms C global variables with storage classes to appropriate Rust declarations:
576    /// - `static` → `static mut` (mutable static)
577    /// - `extern` → `extern "C" { static }`
578    /// - `const` → `const`
579    /// - `static const` → `const` (const is stronger than static)
580    /// - Plain global → `static mut` (default to mutable)
581    ///
582    /// # Examples
583    ///
584    /// ```no_run
585    /// use decy_codegen::CodeGenerator;
586    /// use decy_hir::{HirConstant, HirType, HirExpression};
587    ///
588    /// let codegen = CodeGenerator::new();
589    ///
590    /// // static int counter = 0; → static mut counter: i32 = 0;
591    /// let global = HirConstant::new(
592    ///     "counter".to_string(),
593    ///     HirType::Int,
594    ///     HirExpression::IntLiteral(0),
595    /// );
596    /// let code = codegen.generate_global_variable(&global, true, false, false);
597    /// assert!(code.contains("static mut counter: i32 = 0"));
598    /// ```
599    ///
600    /// # Arguments
601    ///
602    /// * `variable` - The HIR constant representing the global variable
603    /// * `is_static` - Whether the variable has `static` storage class
604    /// * `is_extern` - Whether the variable has `extern` storage class
605    /// * `is_const` - Whether the variable has `const` qualifier
606    ///
607    /// # Safety
608    ///
609    /// Note: `static mut` in Rust requires unsafe blocks to access, which increases
610    /// unsafe usage. However, this is necessary to preserve C semantics for mutable globals.
611    ///
612    /// Reference: ISO C99 §6.7.1 (Storage-class specifiers), K&R §4.2
613    pub fn generate_global_variable(
614        &self,
615        variable: &decy_hir::HirConstant,
616        _is_static: bool,
617        is_extern: bool,
618        is_const: bool,
619    ) -> String {
620        let var_name = variable.name();
621        let value_expr = self.generate_expression(variable.value());
622
623        // Determine Rust type (special handling for string literals)
624        let rust_type = if matches!(
625            variable.const_type(),
626            HirType::Pointer(inner) if matches!(**inner, HirType::Char)
627        ) && is_const
628        {
629            // const char* → &str or &'static str
630            "&str".to_string()
631        } else {
632            Self::map_type(variable.const_type())
633        };
634
635        // Handle different storage class combinations
636        if is_extern {
637            // extern int x; → extern "C" { static x: i32; }
638            format!("extern \"C\" {{\n    static {}: {};\n}}", var_name, rust_type)
639        } else if is_const {
640            // const int x = 10; → const x: i32 = 10;
641            // static const int x = 10; → const x: i32 = 10; (const is stronger)
642            format!("const {}: {} = {};", var_name, rust_type, value_expr)
643        } else {
644            // static int x = 0; → static mut x: i32 = 0;
645            // int x = 0; → static mut x: i32 = 0; (default)
646            // Special handling for arrays: [0; 10] for array initialization
647            let init_expr = if let HirType::Array { element_type, size } = variable.const_type() {
648                if let Some(size_val) = size {
649                    // DECY-201: Fix array initialization for uninitialized arrays
650                    // DECY-246: Use default_value_for_type to handle all types including structs
651                    // Check if value is an integer (likely uninitialized or zero-initialized)
652                    let element_init = match variable.value() {
653                        HirExpression::IntLiteral(_) => {
654                            // Any integer value for struct/complex array → use default
655                            Self::default_value_for_type(element_type)
656                        }
657                        _ => self.generate_expression(variable.value()),
658                    };
659                    format!("[{}; {}]", element_init, size_val)
660                } else {
661                    value_expr
662                }
663            } else if matches!(variable.const_type(), HirType::Pointer(_)) {
664                // Handle NULL pointer initialization
665                if matches!(variable.value(), HirExpression::IntLiteral(0)) {
666                    "std::ptr::null_mut()".to_string()
667                } else {
668                    value_expr
669                }
670            } else {
671                value_expr
672            };
673
674            format!("static mut {}: {} = {};", var_name, rust_type, init_expr)
675        }
676    }
677}