decy_codegen/transform_gen.rs
1//! Transform code generation: annotated signatures, box/vec transforms.
2//! Split from func_gen.rs for PMAT File Health compliance.
3
4use super::*;
5use decy_hir::HirStatement;
6use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedType};
7
8impl CodeGenerator {
9 /// Generate a function signature with lifetime annotations.
10 ///
11 /// Takes an `AnnotatedSignature` with lifetime information and generates
12 /// the complete Rust function signature including lifetime parameters.
13 ///
14 /// # Examples
15 ///
16 /// ```
17 /// use decy_codegen::CodeGenerator;
18 /// use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedParameter, AnnotatedType, LifetimeParam};
19 /// use decy_hir::HirType;
20 ///
21 /// let sig = AnnotatedSignature {
22 /// name: "get_first".to_string(),
23 /// lifetimes: vec![LifetimeParam::standard(0)], // 'a
24 /// parameters: vec![
25 /// AnnotatedParameter {
26 /// name: "items".to_string(),
27 /// param_type: AnnotatedType::Reference {
28 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
29 /// mutable: false,
30 /// lifetime: Some(LifetimeParam::standard(0)),
31 /// },
32 /// },
33 /// ],
34 /// return_type: AnnotatedType::Reference {
35 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
36 /// mutable: false,
37 /// lifetime: Some(LifetimeParam::standard(0)),
38 /// },
39 /// };
40 ///
41 /// let codegen = CodeGenerator::new();
42 /// let rust_sig = codegen.generate_annotated_signature(&sig);
43 ///
44 /// assert!(rust_sig.contains("<'a>"));
45 /// assert!(rust_sig.contains("&'a i32"));
46 /// ```
47 pub fn generate_annotated_signature(&self, sig: &AnnotatedSignature) -> String {
48 self.generate_annotated_signature_with_func(sig, None)
49 }
50
51 /// Generate a function signature from an annotated signature with optional function body access.
52 ///
53 /// When `func` is provided, pointer arithmetic detection is enabled (DECY-123).
54 /// DECY-084: Also detects output parameters for transformation to return values.
55 pub fn generate_annotated_signature_with_func(
56 &self,
57 sig: &AnnotatedSignature,
58 func: Option<&HirFunction>,
59 ) -> String {
60 // DECY-241: Rename functions that conflict with Rust macros/keywords
61 let safe_name = match sig.name.as_str() {
62 "write" => "c_write",
63 "read" => "c_read",
64 "type" => "c_type",
65 "match" => "c_match",
66 "self" => "c_self",
67 "in" => "c_in",
68 name => name,
69 };
70 let mut result = format!("fn {}", safe_name);
71
72 // DECY-084/085: Detect output parameters for transformation
73 let (skip_output_params, output_param_types, output_is_fallible) =
74 Self::detect_output_params(func);
75
76 // DECY-072: Check if we have any non-slice reference parameters that need lifetimes
77 // Slices have elided lifetimes and don't need explicit lifetime parameters
78 let has_non_slice_references = sig.parameters.iter().any(|p| {
79 match &p.param_type {
80 AnnotatedType::Reference { inner, .. } => {
81 // Check if this is NOT a slice (slice = Reference to Array with size=None)
82 !matches!(&**inner, AnnotatedType::Simple(HirType::Array { size: None, .. }))
83 }
84 _ => false,
85 }
86 });
87
88 // Add lifetime parameters only if we have non-slice references
89 if !sig.lifetimes.is_empty() && has_non_slice_references {
90 let lifetime_params: Vec<String> =
91 sig.lifetimes.iter().map(|lt| lt.name.clone()).collect();
92 result.push_str(&format!("<{}>", lifetime_params.join(", ")));
93 }
94
95 // Add function parameters (DECY-084: filter out output params)
96 result.push('(');
97 let params: Vec<String> = sig
98 .parameters
99 .iter()
100 .filter(|p| !skip_output_params.contains(&p.name))
101 .map(|p| self.generate_annotated_param(p, func))
102 .collect();
103 result.push_str(¶ms.join(", "));
104 result.push(')');
105
106 // Generate return type
107 self.append_annotated_return_type(
108 &mut result,
109 sig,
110 func,
111 &output_param_types,
112 output_is_fallible,
113 );
114
115 result
116 }
117
118 /// Detect output parameters from a function for signature transformation.
119 /// Returns (skip_set, output_types, is_fallible).
120 fn detect_output_params(
121 func: Option<&HirFunction>,
122 ) -> (std::collections::HashSet<String>, Vec<HirType>, bool) {
123 use decy_analyzer::output_params::{OutputParamDetector, ParameterKind};
124 let mut skip_output_params = std::collections::HashSet::new();
125 let mut output_param_types: Vec<HirType> = Vec::new();
126 let mut output_is_fallible = false;
127
128 if let Some(f) = func {
129 let output_detector = OutputParamDetector::new();
130 let output_params = output_detector.detect(f);
131
132 // Count non-pointer parameters (inputs)
133 let input_param_count = f
134 .parameters()
135 .iter()
136 .filter(|p| !matches!(p.param_type(), HirType::Pointer(_)))
137 .count();
138
139 // Count potential output params for heuristic
140 let output_param_count =
141 output_params.iter().filter(|op| op.kind == ParameterKind::Output).count();
142
143 for op in &output_params {
144 if op.kind == ParameterKind::Output {
145 // Heuristic: Only treat as output param if:
146 // 1. There are other input parameters (output is derived from inputs)
147 // 2. Or, the name suggests it's an output (result, out, output, ret, etc.)
148 // 3. DECY-085: Or, there are multiple output params (void func with multiple outs)
149 let is_output_name = Self::is_output_param_name(&op.name);
150
151 if input_param_count > 0 || is_output_name || output_param_count >= 2 {
152 skip_output_params.insert(op.name.clone());
153 output_is_fallible = op.is_fallible;
154 // DECY-085: Collect all output parameter types
155 if let Some(param) = f.parameters().iter().find(|p| p.name() == op.name) {
156 if let HirType::Pointer(inner) = param.param_type() {
157 output_param_types.push((**inner).clone());
158 }
159 }
160 }
161 }
162 }
163 }
164
165 (skip_output_params, output_param_types, output_is_fallible)
166 }
167
168 /// Check if a parameter name suggests it is an output parameter.
169 fn is_output_param_name(name: &str) -> bool {
170 let name_lower = name.to_lowercase();
171 name_lower.contains("result")
172 || name_lower.contains("out")
173 || name_lower.contains("ret")
174 || name_lower == "len"
175 || name_lower == "size"
176 || name_lower == "x"
177 || name_lower == "y"
178 || name_lower == "z"
179 || name_lower == "w"
180 || name_lower == "h"
181 || name_lower == "width"
182 || name_lower == "height"
183 || name_lower == "r"
184 || name_lower == "g"
185 || name_lower == "b"
186 || name_lower == "count"
187 || name_lower == "avg"
188 }
189
190 /// Generate a single annotated parameter string.
191 fn generate_annotated_param(
192 &self,
193 p: &decy_ownership::lifetime_gen::AnnotatedParameter,
194 func: Option<&HirFunction>,
195 ) -> String {
196 // Check if this is a slice parameter (Reference to Array with size=None)
197 let is_slice = match &p.param_type {
198 AnnotatedType::Reference { inner, .. } => match &**inner {
199 AnnotatedType::Simple(HirType::Array { size, .. }) => size.is_none(),
200 _ => false,
201 },
202 _ => false,
203 };
204
205 if is_slice {
206 // DECY-072: Slices don't need 'mut' prefix or explicit lifetimes
207 let type_str = match &p.param_type {
208 AnnotatedType::Reference { inner, mutable, .. } => {
209 if let AnnotatedType::Simple(HirType::Array { element_type, .. }) =
210 &**inner
211 {
212 if *mutable {
213 format!("&mut [{}]", Self::map_type(element_type))
214 } else {
215 format!("&[{}]", Self::map_type(element_type))
216 }
217 } else {
218 self.annotated_type_to_string(&p.param_type)
219 }
220 }
221 _ => self.annotated_type_to_string(&p.param_type),
222 };
223 return format!("{}: {}", p.name, type_str);
224 }
225
226 // DECY-111: Transform pointer parameters to mutable references
227 // DECY-123: Skip transformation if pointer arithmetic is used
228 if let AnnotatedType::Simple(HirType::Pointer(inner)) = &p.param_type {
229 return self.generate_annotated_pointer_param(&p.name, inner, func);
230 }
231
232 // DECY-196: Handle unsized array parameters → slice references
233 if let AnnotatedType::Simple(HirType::Array { element_type, size: None }) =
234 &p.param_type
235 {
236 let element_str = Self::map_type(element_type);
237 return format!("{}: &mut [{}]", p.name, element_str);
238 }
239
240 // DECY-041: Add mut for all non-slice parameters to match C semantics
241 format!("mut {}: {}", p.name, self.annotated_type_to_string(&p.param_type))
242 }
243
244 /// Generate an annotated pointer parameter (reference, raw pointer, slice, or &str).
245 fn generate_annotated_pointer_param(
246 &self,
247 name: &str,
248 inner: &HirType,
249 func: Option<&HirFunction>,
250 ) -> String {
251 // DECY-135: const char* → &str transformation
252 if let Some(f) = func {
253 if let Some(orig_param) =
254 f.parameters().iter().find(|fp| fp.name() == name)
255 {
256 if orig_param.is_const_char_pointer() {
257 return format!("mut {}: &str", name);
258 }
259 }
260 }
261 // DECY-134: Check for string iteration pattern FIRST
262 if let Some(f) = func {
263 if self.is_string_iteration_param(f, name) {
264 let is_mutable = self.is_parameter_deref_modified(f, name);
265 if is_mutable {
266 return format!("{}: &mut [u8]", name);
267 } else {
268 return format!("{}: &[u8]", name);
269 }
270 }
271 }
272 // DECY-123: If we have function body access, check for pointer arithmetic
273 if let Some(f) = func {
274 if self.uses_pointer_arithmetic(f, name) {
275 let inner_type = Self::map_type(inner);
276 return format!("mut {}: *mut {}", name, inner_type);
277 }
278 }
279 // DECY-168: void* parameters should stay as raw pointers
280 if matches!(*inner, HirType::Void) {
281 return format!("{}: *mut ()", name);
282 }
283 // Transform *mut T → &mut T for safety
284 let inner_type = Self::map_type(inner);
285 format!("{}: &mut {}", name, inner_type)
286 }
287
288 /// Append return type for annotated signature.
289 fn append_annotated_return_type(
290 &self,
291 result: &mut String,
292 sig: &AnnotatedSignature,
293 func: Option<&HirFunction>,
294 output_param_types: &[HirType],
295 output_is_fallible: bool,
296 ) {
297 // Special handling for main function (DECY-AUDIT-001)
298 let return_type_str = self.annotated_type_to_string(&sig.return_type);
299 if sig.name == "main" && return_type_str == "i32" {
300 return;
301 }
302
303 // DECY-084/085: Generate return type considering output parameters
304 if !output_param_types.is_empty() {
305 let out_type_str = if output_param_types.len() == 1 {
306 Self::map_type(&output_param_types[0])
307 } else {
308 let type_strs: Vec<String> =
309 output_param_types.iter().map(Self::map_type).collect();
310 format!("({})", type_strs.join(", "))
311 };
312
313 if output_is_fallible {
314 result.push_str(&format!(" -> Result<{}, i32>", out_type_str));
315 } else {
316 result.push_str(&format!(" -> {}", out_type_str));
317 }
318 } else {
319 // DECY-142: Check for Vec return type (malloc'd array returns)
320 if let Some(f) = func {
321 if let Some(vec_element_type) = self.detect_vec_return(f) {
322 let element_type_str = Self::map_type(&vec_element_type);
323 result.push_str(&format!(" -> Vec<{}>", element_type_str));
324 return;
325 }
326 }
327 // Add return type if not void
328 if return_type_str != "()" {
329 result.push_str(&format!(" -> {}", return_type_str));
330 }
331 }
332 }
333
334 /// Convert an `AnnotatedType` to Rust type string with lifetime annotations.
335 ///
336 /// # Examples
337 ///
338 /// ```
339 /// use decy_codegen::CodeGenerator;
340 /// use decy_ownership::lifetime_gen::{AnnotatedType, LifetimeParam};
341 /// use decy_hir::HirType;
342 ///
343 /// let codegen = CodeGenerator::new();
344 ///
345 /// // Simple type
346 /// let simple = AnnotatedType::Simple(HirType::Int);
347 /// assert_eq!(codegen.annotated_type_to_string(&simple), "i32");
348 ///
349 /// // Reference with lifetime
350 /// let ref_type = AnnotatedType::Reference {
351 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
352 /// mutable: false,
353 /// lifetime: Some(LifetimeParam::standard(0)),
354 /// };
355 /// assert_eq!(codegen.annotated_type_to_string(&ref_type), "&'a i32");
356 /// ```
357 #[allow(clippy::only_used_in_recursion)]
358 pub fn annotated_type_to_string(&self, annotated_type: &AnnotatedType) -> String {
359 match annotated_type {
360 AnnotatedType::Simple(hir_type) => Self::map_type(hir_type),
361 AnnotatedType::Reference { inner, mutable, lifetime } => {
362 // DECY-072: Special case for slices: &Vec<T> → &[T]
363 // Check if inner is a Vec type
364 if let AnnotatedType::Simple(HirType::Vec(element_type)) = &**inner {
365 let element_str = Self::map_type(element_type);
366 if *mutable {
367 return format!("&mut [{}]", element_str);
368 } else {
369 return format!("&[{}]", element_str);
370 }
371 }
372
373 let mut result = String::from("&");
374
375 // Add lifetime if present
376 if let Some(lt) = lifetime {
377 result.push_str(<.name);
378 result.push(' ');
379 }
380
381 // Add mutability
382 if *mutable {
383 result.push_str("mut ");
384 }
385
386 // Add inner type
387 result.push_str(&self.annotated_type_to_string(inner));
388
389 result
390 }
391 }
392 }
393
394 /// Generate a default return statement for a type.
395 ///
396 /// # Examples
397 ///
398 /// ```
399 /// use decy_codegen::CodeGenerator;
400 /// use decy_hir::HirType;
401 ///
402 /// let codegen = CodeGenerator::new();
403 /// assert!(codegen.generate_return(&HirType::Int).contains("return 0"));
404 /// ```
405 pub fn generate_return(&self, return_type: &HirType) -> String {
406 match return_type {
407 HirType::Void => String::new(),
408 HirType::Bool => " return false;".to_string(),
409 HirType::Int => " return 0;".to_string(),
410 HirType::UnsignedInt => " return 0;".to_string(), // DECY-158
411 HirType::Float => " return 0.0;".to_string(),
412 HirType::Double => " return 0.0;".to_string(),
413 HirType::Char => " return 0;".to_string(),
414 HirType::SignedChar => " return 0;".to_string(), // DECY-250
415 HirType::Pointer(_) => " return std::ptr::null_mut();".to_string(),
416 HirType::Box(inner) => {
417 format!(" return Box::new({});", Self::default_value_for_type(inner))
418 }
419 HirType::Vec(_) => " return Vec::new();".to_string(),
420 HirType::Option(_) => " return None;".to_string(),
421 HirType::Reference { .. } => {
422 // References in return position need concrete values from parameters
423 // This should be handled by lifetime-annotated code generation
424 // using generate_function_with_lifetimes() instead
425 String::new()
426 }
427 HirType::Struct(name) => {
428 format!(" return {}::default();", name)
429 }
430 HirType::Enum(name) => {
431 format!(" return {}::default();", name)
432 }
433 HirType::Array { element_type, size } => {
434 if let Some(n) = size {
435 format!(" return [{}; {}];", Self::default_value_for_type(element_type), n)
436 } else {
437 // Unsized arrays in return position don't make sense
438 String::new()
439 }
440 }
441 HirType::FunctionPointer { .. } => {
442 // Function pointers in return position need concrete function values
443 // This should be handled by the function body
444 String::new()
445 }
446 HirType::StringLiteral => r#" return "";"#.to_string(),
447 HirType::OwnedString => " return String::new();".to_string(),
448 HirType::StringReference => r#" return "";"#.to_string(),
449 HirType::Union(_) => {
450 // Unions will be transformed to enums
451 // Return statement depends on the specific enum variant
452 String::new()
453 }
454 // DECY-172: Type aliases return 0
455 HirType::TypeAlias(name) => match name.as_str() {
456 "size_t" => " return 0usize;".to_string(),
457 "ssize_t" | "ptrdiff_t" => " return 0isize;".to_string(),
458 _ => " return 0;".to_string(),
459 },
460 }
461 }
462
463 /// Generate a complete function from HIR.
464 ///
465 /// # Examples
466 ///
467 /// ```
468 /// use decy_codegen::CodeGenerator;
469 /// use decy_hir::{HirFunction, HirType, HirParameter};
470 ///
471 /// let func = HirFunction::new(
472 /// "add".to_string(),
473 /// HirType::Int,
474 /// vec![
475 /// HirParameter::new("a".to_string(), HirType::Int),
476 /// HirParameter::new("b".to_string(), HirType::Int),
477 /// ],
478 /// );
479 ///
480 /// let codegen = CodeGenerator::new();
481 /// let code = codegen.generate_function(&func);
482 ///
483 /// assert!(code.contains("fn add(mut a: i32, mut b: i32) -> i32"));
484 /// assert!(code.contains("{"));
485 /// assert!(code.contains("}"));
486 /// ```
487 pub fn generate_function(&self, func: &HirFunction) -> String {
488 // DECY-211: CUDA __global__ kernels -> extern "C" FFI wrapper
489 if func.cuda_qualifier() == Some(decy_hir::HirCudaQualifier::Global) {
490 return self.generate_cuda_kernel_ffi(func);
491 }
492 // DECY-211: CUDA __device__ functions -> comment noting device-only
493 if func.cuda_qualifier() == Some(decy_hir::HirCudaQualifier::Device) {
494 let sig = self.generate_signature(func);
495 return format!(
496 "// CUDA __device__ function — runs on GPU only, not transpiled\n// {}\n",
497 sig
498 );
499 }
500
501 let mut code = String::new();
502
503 // DECY-072 GREEN: Build mapping of length params -> array params for body transformation
504 use decy_ownership::dataflow::DataflowAnalyzer;
505 let analyzer = DataflowAnalyzer::new();
506 let graph = analyzer.analyze(func);
507
508 let mut length_to_array: std::collections::HashMap<String, String> =
509 std::collections::HashMap::new();
510
511 // DECY-113: Only map length params with length-like names
512 // DECY-162: Don't map length params when array uses pointer arithmetic (stays raw pointer)
513 for (idx, param) in func.parameters().iter().enumerate() {
514 if let Some(true) = graph.is_array_parameter(param.name()) {
515 // DECY-162: Skip if array param uses pointer arithmetic
516 // Raw pointers don't have .len(), so we keep the size param as-is
517 if self.uses_pointer_arithmetic(func, param.name()) {
518 continue;
519 }
520
521 // This is an array parameter - map the next param to this array
522 // but only if it has a length-like name
523 if idx + 1 < func.parameters().len() {
524 let next_param = &func.parameters()[idx + 1];
525 if matches!(next_param.param_type(), HirType::Int) {
526 let param_name = next_param.name().to_lowercase();
527 if param_name.contains("len")
528 || param_name.contains("size")
529 || param_name.contains("count")
530 || param_name == "n"
531 || param_name == "num"
532 {
533 length_to_array
534 .insert(next_param.name().to_string(), param.name().to_string());
535 }
536 }
537 }
538 }
539 }
540
541 // Generate signature
542 code.push_str(&self.generate_signature(func));
543 code.push_str(" {\n");
544
545 // Initialize type context for tracking variable types across statements
546 let mut ctx = TypeContext::from_function(func);
547
548 // DECY-129/DECY-148: Update context to reflect pointer-to-reference transformations
549 // When pointer params are transformed to &mut T in signature, context must match
550 // DECY-148: Distinguish array params (slices) from struct pointer params (references)
551 for param in func.parameters() {
552 if let HirType::Pointer(inner) = param.param_type() {
553 // Check if this pointer uses pointer arithmetic (keep as raw pointer)
554 if !self.uses_pointer_arithmetic(func, param.name()) {
555 // DECY-148: Check if this is an ARRAY parameter
556 let is_array_param = graph.is_array_parameter(param.name()).unwrap_or(false);
557
558 if is_array_param {
559 // Array parameter → register as slice (Reference to Array)
560 ctx.add_variable(
561 param.name().to_string(),
562 HirType::Reference {
563 inner: Box::new(HirType::Array {
564 element_type: inner.clone(),
565 size: None, // Slice (unsized array)
566 }),
567 mutable: true,
568 },
569 );
570 } else {
571 // Struct pointer → register as Reference to inner type
572 let is_mutable = self.is_parameter_deref_modified(func, param.name());
573 ctx.add_variable(
574 param.name().to_string(),
575 HirType::Reference { inner: inner.clone(), mutable: is_mutable },
576 );
577 }
578 }
579 }
580 }
581
582 // DECY-142: Detect Vec-return functions for correct return type handling
583 let effective_return_type = if let Some(element_type) = self.detect_vec_return(func) {
584 HirType::Vec(Box::new(element_type))
585 } else {
586 func.return_type().clone()
587 };
588
589 // Generate body statements if present
590 if func.body().is_empty() {
591 // Generate stub body with return statement
592 let return_stmt = self.generate_return(func.return_type());
593 if !return_stmt.is_empty() {
594 code.push_str(&return_stmt);
595 code.push('\n');
596 }
597 } else {
598 // Generate actual body statements with persistent context
599 for stmt in func.body() {
600 code.push_str(" ");
601 let stmt_code = self.generate_statement_with_context(
602 stmt,
603 Some(func.name()),
604 &mut ctx,
605 Some(&effective_return_type),
606 );
607
608 // DECY-072 GREEN: Replace length parameter references with arr.len() calls
609 let transformed = self.transform_length_refs(&stmt_code, &length_to_array);
610 code.push_str(&transformed);
611 code.push('\n');
612 }
613 }
614
615 code.push('}');
616 code
617 }
618
619 /// Generate a complete function from HIR with struct definitions for type inference.
620 ///
621 /// This is useful for testing when struct fields need proper type inference.
622 /// DECY-165: Enables proper raw pointer detection for struct field access.
623 pub fn generate_function_with_structs(
624 &self,
625 func: &HirFunction,
626 structs: &[decy_hir::HirStruct],
627 ) -> String {
628 // DECY-221: CUDA kernel/device functions bypass normal codegen
629 if func.cuda_qualifier() == Some(decy_hir::HirCudaQualifier::Global) {
630 return self.generate_cuda_kernel_ffi(func);
631 }
632 if func.cuda_qualifier() == Some(decy_hir::HirCudaQualifier::Device) {
633 let sig = self.generate_signature(func);
634 return format!("// CUDA __device__ function — GPU only\n// {}\n", sig);
635 }
636
637 let mut code = String::new();
638
639 // Generate signature
640 code.push_str(&self.generate_signature(func));
641 code.push_str(" {\n");
642
643 // Initialize type context with function parameters AND struct definitions
644 let mut ctx = TypeContext::from_function(func);
645
646 // DECY-165: Add struct definitions to context for field type lookup
647 for struct_def in structs {
648 ctx.add_struct(struct_def);
649 }
650
651 // DECY-129/DECY-148: Update context to reflect pointer-to-reference transformations
652 // When pointer params are transformed to &mut T in signature, context must match
653 use decy_ownership::dataflow::DataflowAnalyzer;
654 let analyzer = DataflowAnalyzer::new();
655 let graph = analyzer.analyze(func);
656
657 for param in func.parameters() {
658 if let HirType::Pointer(inner) = param.param_type() {
659 // Only transform if the pointer is not used for pointer arithmetic
660 if !self.uses_pointer_arithmetic(func, param.name()) {
661 // Check if it's an array parameter → use &[T] or &mut [T]
662 if graph.is_array_parameter(param.name()) == Some(true) {
663 // Use slice reference type
664 ctx.add_variable(
665 param.name().to_string(),
666 HirType::Reference {
667 inner: Box::new(HirType::Vec(inner.clone())),
668 mutable: self.is_parameter_deref_modified(func, param.name()),
669 },
670 );
671 } else {
672 // Single pointer → reference
673 ctx.add_variable(
674 param.name().to_string(),
675 HirType::Reference {
676 inner: inner.clone(),
677 mutable: self.is_parameter_deref_modified(func, param.name()),
678 },
679 );
680 }
681 }
682 }
683 }
684
685 // Generate body statements
686 if !func.body().is_empty() {
687 for stmt in func.body() {
688 code.push_str(" ");
689 let stmt_code = self.generate_statement_with_context(
690 stmt,
691 Some(func.name()),
692 &mut ctx,
693 Some(func.return_type()),
694 );
695 code.push_str(&stmt_code);
696 code.push('\n');
697 }
698 }
699
700 code.push('}');
701 code
702 }
703
704 /// Generate a complete function from HIR with lifetime annotations.
705 ///
706 /// Takes both the HIR function and its annotated signature to generate
707 /// Rust code with proper lifetime annotations.
708 ///
709 /// # Examples
710 ///
711 /// ```
712 /// use decy_codegen::CodeGenerator;
713 /// use decy_hir::{HirFunction, HirType, HirParameter};
714 /// use decy_ownership::lifetime_gen::{AnnotatedSignature, AnnotatedParameter, AnnotatedType, LifetimeParam};
715 ///
716 /// let func = HirFunction::new(
717 /// "identity".to_string(),
718 /// HirType::Reference {
719 /// inner: Box::new(HirType::Int),
720 /// mutable: false,
721 /// },
722 /// vec![
723 /// HirParameter::new("x".to_string(), HirType::Reference {
724 /// inner: Box::new(HirType::Int),
725 /// mutable: false,
726 /// }),
727 /// ],
728 /// );
729 ///
730 /// let sig = AnnotatedSignature {
731 /// name: "identity".to_string(),
732 /// lifetimes: vec![LifetimeParam::standard(0)],
733 /// parameters: vec![
734 /// AnnotatedParameter {
735 /// name: "x".to_string(),
736 /// param_type: AnnotatedType::Reference {
737 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
738 /// mutable: false,
739 /// lifetime: Some(LifetimeParam::standard(0)),
740 /// },
741 /// },
742 /// ],
743 /// return_type: AnnotatedType::Reference {
744 /// inner: Box::new(AnnotatedType::Simple(HirType::Int)),
745 /// mutable: false,
746 /// lifetime: Some(LifetimeParam::standard(0)),
747 /// },
748 /// };
749 ///
750 /// let codegen = CodeGenerator::new();
751 /// let code = codegen.generate_function_with_lifetimes(&func, &sig);
752 ///
753 /// assert!(code.contains("<'a>"));
754 /// assert!(code.contains("&'a i32"));
755 /// ```
756 pub fn generate_function_with_lifetimes(
757 &self,
758 func: &HirFunction,
759 sig: &AnnotatedSignature,
760 ) -> String {
761 self.generate_function_with_lifetimes_and_structs(func, sig, &[], &[], &[], &[], &[])
762 }
763
764 /// Generate a complete function from HIR with lifetime annotations and struct definitions.
765 ///
766 /// Takes the HIR function, its annotated signature, struct definitions, and all function
767 /// signatures for call site reference mutability.
768 ///
769 /// # Arguments
770 /// * `func` - The HIR function to generate
771 /// * `sig` - The annotated signature with lifetime annotations
772 /// * `structs` - Struct definitions for field type awareness
773 /// * `all_functions` - All function signatures for DECY-117 call site mutability
774 /// * `slice_func_args` - DECY-116: func_name -> [(array_idx, len_idx)] for call site transformation
775 /// * `string_iter_funcs` - DECY-134b: func_name -> [(param_idx, is_mutable)] for string iteration
776 /// * `globals` - DECY-220/233: Global variable names and types for unsafe access and type inference
777 #[allow(clippy::too_many_arguments)]
778 pub fn generate_function_with_lifetimes_and_structs(
779 &self,
780 func: &HirFunction,
781 sig: &AnnotatedSignature,
782 structs: &[decy_hir::HirStruct],
783 all_functions: &[(String, Vec<HirType>)],
784 slice_func_args: &[(String, Vec<(usize, usize)>)],
785 string_iter_funcs: &[(String, Vec<(usize, bool)>)],
786 globals: &[(String, HirType)],
787 ) -> String {
788 contract_pre_host_transpilation!();
789 // DECY-221: CUDA kernel/device functions bypass normal codegen
790 if func.cuda_qualifier() == Some(decy_hir::HirCudaQualifier::Global) {
791 return self.generate_cuda_kernel_ffi(func);
792 }
793 if func.cuda_qualifier() == Some(decy_hir::HirCudaQualifier::Device) {
794 let sig_str = self.generate_signature(func);
795 return format!(
796 "// CUDA __device__ function — runs on GPU only, not transpiled\n// {}\n",
797 sig_str
798 );
799 }
800
801 let mut code = String::new();
802
803 // Generate signature with lifetimes
804 // DECY-123: Pass function for pointer arithmetic detection
805 code.push_str(&self.generate_annotated_signature_with_func(sig, Some(func)));
806 code.push_str(" {\n");
807
808 // DECY-041: Initialize type context with function parameters for pointer arithmetic
809 let mut ctx = TypeContext::from_function(func);
810
811 // DECY-220/233: Register global variables for unsafe access tracking and type inference
812 for (name, var_type) in globals {
813 ctx.add_global(name.clone());
814 ctx.add_variable(name.clone(), var_type.clone());
815 }
816
817 // DECY-134: Track string iteration params for index-based body generation
818 let mut string_iter_index_decls = Vec::new();
819
820 // DECY-111: Transform pointer parameters to references in the context
821 // DECY-123/124: Only transform if NOT using pointer arithmetic
822 // This prevents unsafe blocks from being generated for reference dereferences
823 // DECY-148: Use DataflowAnalyzer to determine which params are array params
824 use decy_ownership::dataflow::DataflowAnalyzer;
825 let analyzer = DataflowAnalyzer::new();
826 let graph = analyzer.analyze(func);
827
828 for param in func.parameters() {
829 // DECY-138: Check for const char* → &str transformation FIRST
830 // This enables proper string iteration pattern codegen
831 if param.is_const_char_pointer() {
832 ctx.add_variable(param.name().to_string(), HirType::StringReference);
833 } else if let HirType::Pointer(inner) = param.param_type() {
834 // DECY-134: Check for string iteration pattern FIRST
835 if self.is_string_iteration_param(func, param.name()) {
836 // Register as Vec type in context (slice in generated code)
837 ctx.add_variable(param.name().to_string(), HirType::Vec(inner.clone()));
838 // Register string iteration param with index variable
839 let idx_var = format!("{}_idx", param.name());
840 ctx.add_string_iter_param(param.name().to_string(), idx_var.clone());
841 // Add index declaration to generate at function start
842 string_iter_index_decls.push(format!(" let mut {}: usize = 0;", idx_var));
843 } else if self.uses_pointer_arithmetic(func, param.name()) {
844 // DECY-124: Keep as pointer in context if pointer arithmetic is used
845 // This ensures proper unsafe wrapping_add/wrapping_sub codegen
846 // Keep as pointer - codegen will generate unsafe blocks
847 ctx.add_variable(param.name().to_string(), param.param_type().clone());
848 } else {
849 // DECY-148: Check if this is an ARRAY parameter (detected by dataflow analysis)
850 let is_array_param = graph.is_array_parameter(param.name()).unwrap_or(false);
851
852 if is_array_param {
853 // DECY-146: Array parameter → register as slice (Reference to Array)
854 // This enables proper .as_ptr()/.as_mut_ptr() generation
855 ctx.add_variable(
856 param.name().to_string(),
857 HirType::Reference {
858 inner: Box::new(HirType::Array {
859 element_type: inner.clone(),
860 size: None, // Slice (unsized array)
861 }),
862 mutable: true,
863 },
864 );
865 } else {
866 // DECY-148: Non-array struct pointer → register as Reference to inner type
867 // This enables proper `&mut T as *mut _` coercion on return
868 let is_mutable = self.is_parameter_deref_modified(func, param.name());
869 ctx.add_variable(
870 param.name().to_string(),
871 HirType::Reference { inner: inner.clone(), mutable: is_mutable },
872 );
873 }
874 }
875 }
876 }
877
878 // DECY-134: Generate index variable declarations for string iteration params
879 for decl in &string_iter_index_decls {
880 code.push_str(decl);
881 code.push('\n');
882 }
883
884 // Add struct definitions to context for field type lookup
885 for struct_def in structs {
886 ctx.add_struct(struct_def);
887 }
888
889 // DECY-117: Add all function signatures for call site reference mutability
890 for (func_name, param_types) in all_functions {
891 ctx.add_function(func_name.clone(), param_types.clone());
892 }
893
894 // DECY-116: Add slice function arg mappings for call site transformation
895 for (func_name, arg_mappings) in slice_func_args {
896 ctx.add_slice_func_args(func_name.clone(), arg_mappings.clone());
897 }
898
899 // DECY-134b: Add string iteration function info for call site transformation
900 for (func_name, params) in string_iter_funcs {
901 ctx.add_string_iter_func(func_name.clone(), params.clone());
902 }
903
904 // DECY-142: Detect Vec-return functions for correct return type handling
905 let effective_return_type = if let Some(element_type) = self.detect_vec_return(func) {
906 HirType::Vec(Box::new(element_type))
907 } else {
908 func.return_type().clone()
909 };
910
911 // Generate body statements if present
912 if func.body().is_empty() {
913 // Generate stub body with return statement
914 let return_stmt = self.generate_return(func.return_type());
915 if !return_stmt.is_empty() {
916 code.push_str(&return_stmt);
917 code.push('\n');
918 }
919 } else {
920 // Generate actual body statements with type context and return type
921 for stmt in func.body() {
922 code.push_str(" ");
923 code.push_str(&self.generate_statement_with_context(
924 stmt,
925 Some(func.name()),
926 &mut ctx,
927 Some(&effective_return_type),
928 ));
929 code.push('\n');
930 }
931 }
932
933 code.push('}');
934 code
935 }
936
937 /// Generate a function with Box transformations applied.
938 ///
939 /// This method analyzes the function for malloc/free patterns and
940 /// transforms them into safe `Box::new()` expressions.
941 ///
942 /// # Examples
943 ///
944 /// ```
945 /// use decy_codegen::CodeGenerator;
946 /// use decy_hir::{HirFunction, HirType, HirStatement, HirExpression};
947 /// use decy_analyzer::patterns::PatternDetector;
948 ///
949 /// let func = HirFunction::new_with_body(
950 /// "test".to_string(),
951 /// HirType::Void,
952 /// vec![],
953 /// vec![
954 /// HirStatement::VariableDeclaration {
955 /// name: "ptr".to_string(),
956 /// var_type: HirType::Pointer(Box::new(HirType::Int)),
957 /// initializer: Some(HirExpression::FunctionCall {
958 /// function: "malloc".to_string(),
959 /// arguments: vec![HirExpression::IntLiteral(100)],
960 /// }),
961 /// },
962 /// ],
963 /// );
964 ///
965 /// let codegen = CodeGenerator::new();
966 /// let detector = PatternDetector::new();
967 /// let candidates = detector.find_box_candidates(&func);
968 /// let code = codegen.generate_function_with_box_transform(&func, &candidates);
969 ///
970 /// assert!(code.contains("Box::new"));
971 /// ```
972 pub fn generate_function_with_box_transform(
973 &self,
974 func: &HirFunction,
975 candidates: &[decy_analyzer::patterns::BoxCandidate],
976 ) -> String {
977 let mut code = String::new();
978
979 // Generate signature
980 code.push_str(&self.generate_signature(func));
981 code.push_str(" {\n");
982
983 // Generate body statements if present
984 if func.body().is_empty() {
985 // Generate stub body with return statement
986 let return_stmt = self.generate_return(func.return_type());
987 if !return_stmt.is_empty() {
988 code.push_str(&return_stmt);
989 code.push('\n');
990 }
991 } else {
992 // Generate body statements with Box transformations
993 for (idx, stmt) in func.body().iter().enumerate() {
994 // Check if this statement should be transformed
995 let transformed_stmt =
996 if let Some(candidate) = candidates.iter().find(|c| c.malloc_index == idx) {
997 self.box_transformer.transform_statement(stmt, candidate)
998 } else {
999 stmt.clone()
1000 };
1001
1002 code.push_str(" ");
1003 code.push_str(
1004 &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
1005 );
1006 code.push('\n');
1007 }
1008 }
1009
1010 code.push('}');
1011 code
1012 }
1013
1014 /// Generate a function with Vec transformations applied.
1015 ///
1016 /// This method analyzes the function for malloc(n * sizeof(T)) patterns and
1017 /// transforms them into safe `Vec::with_capacity(n)` expressions.
1018 pub fn generate_function_with_vec_transform(
1019 &self,
1020 func: &HirFunction,
1021 candidates: &[decy_analyzer::patterns::VecCandidate],
1022 ) -> String {
1023 let mut code = String::new();
1024
1025 // Generate signature
1026 code.push_str(&self.generate_signature(func));
1027 code.push_str(" {\n");
1028
1029 // Generate body statements if present
1030 if func.body().is_empty() {
1031 // Generate stub body with return statement
1032 let return_stmt = self.generate_return(func.return_type());
1033 if !return_stmt.is_empty() {
1034 code.push_str(&return_stmt);
1035 code.push('\n');
1036 }
1037 } else {
1038 // Generate body statements with Vec transformations
1039 for (idx, stmt) in func.body().iter().enumerate() {
1040 // Check if this statement should be transformed
1041 let transformed_stmt =
1042 if let Some(candidate) = candidates.iter().find(|c| c.malloc_index == idx) {
1043 self.transform_vec_statement(stmt, candidate)
1044 } else {
1045 stmt.clone()
1046 };
1047
1048 code.push_str(" ");
1049 code.push_str(
1050 &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
1051 );
1052 code.push('\n');
1053 }
1054 }
1055
1056 code.push('}');
1057 code
1058 }
1059
1060 /// Transform a statement to use Vec instead of malloc for array patterns.
1061 pub(crate) fn transform_vec_statement(
1062 &self,
1063 stmt: &HirStatement,
1064 candidate: &decy_analyzer::patterns::VecCandidate,
1065 ) -> HirStatement {
1066 match stmt {
1067 HirStatement::VariableDeclaration { name, var_type, initializer: _ } => {
1068 // Get the element type from the pointer
1069 let element_type = if let HirType::Pointer(inner) = var_type {
1070 (**inner).clone()
1071 } else {
1072 // Fallback: keep original type
1073 return stmt.clone();
1074 };
1075
1076 // Transform type to Vec
1077 let vec_type = HirType::Vec(Box::new(element_type));
1078
1079 // Transform initializer: malloc(n * sizeof(T)) → Vec::with_capacity(n)
1080 let vec_initializer = if let Some(capacity_expr) = &candidate.capacity_expr {
1081 Some(HirExpression::FunctionCall {
1082 function: "Vec::with_capacity".to_string(),
1083 arguments: vec![capacity_expr.clone()],
1084 })
1085 } else {
1086 // No capacity expression - use Vec::new()
1087 Some(HirExpression::FunctionCall {
1088 function: "Vec::new".to_string(),
1089 arguments: vec![],
1090 })
1091 };
1092
1093 HirStatement::VariableDeclaration {
1094 name: name.clone(),
1095 var_type: vec_type,
1096 initializer: vec_initializer,
1097 }
1098 }
1099 HirStatement::Assignment { target: _, value: _ } => {
1100 // Similar transformation for assignments
1101 // For now, keep the original statement
1102 // Future: handle ptr = malloc(n * sizeof(T)) assignments
1103 stmt.clone()
1104 }
1105 _ => stmt.clone(),
1106 }
1107 }
1108
1109 /// Generate a function with both Box and Vec transformations applied.
1110 ///
1111 /// This method combines both Box and Vec transformations,
1112 /// applying them to their respective patterns.
1113 pub fn generate_function_with_box_and_vec_transform(
1114 &self,
1115 func: &HirFunction,
1116 box_candidates: &[decy_analyzer::patterns::BoxCandidate],
1117 vec_candidates: &[decy_analyzer::patterns::VecCandidate],
1118 ) -> String {
1119 let mut code = String::new();
1120
1121 // Generate signature
1122 code.push_str(&self.generate_signature(func));
1123 code.push_str(" {\n");
1124
1125 // Generate body statements if present
1126 if func.body().is_empty() {
1127 // Generate stub body with return statement
1128 let return_stmt = self.generate_return(func.return_type());
1129 if !return_stmt.is_empty() {
1130 code.push_str(&return_stmt);
1131 code.push('\n');
1132 }
1133 } else {
1134 // Generate body statements with both transformations
1135 for (idx, stmt) in func.body().iter().enumerate() {
1136 // Check Vec candidates first (more specific pattern)
1137 let transformed_stmt = if let Some(vec_candidate) =
1138 vec_candidates.iter().find(|c| c.malloc_index == idx)
1139 {
1140 self.transform_vec_statement(stmt, vec_candidate)
1141 } else if let Some(box_candidate) =
1142 box_candidates.iter().find(|c| c.malloc_index == idx)
1143 {
1144 self.box_transformer.transform_statement(stmt, box_candidate)
1145 } else {
1146 stmt.clone()
1147 };
1148
1149 code.push_str(" ");
1150 code.push_str(
1151 &self.generate_statement_for_function(&transformed_stmt, Some(func.name())),
1152 );
1153 code.push('\n');
1154 }
1155 }
1156
1157 code.push('}');
1158 code
1159 }
1160}