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}