1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! DECY-118: Local pointer variable to reference transformation tests.
//!
//! DECY-118 is DISABLED for local variables due to DECY-128 (double pointer issues).
//! When a local pointer's address is taken (e.g., &p in set_value(&p, 42)),
//! transforming `int *p = &x` to `let p = &mut x` breaks type compatibility.
//!
//! Current behavior: Local pointer → raw pointer (requires unsafe)
//! Future (when DECY-118 re-enabled): Local pointer → reference
//!
//! C: int *p = &x;
//! Current Rust: let p: *mut i32 = &mut x as *mut i32;
//! Future Rust: let p = &mut x;
use decy_core::transpile;
/// Test that local pointer initialized with address-of generates raw pointer.
///
/// DECY-118 is DISABLED. Local pointers remain as raw pointers because
/// their address might be taken (e.g., &p), which would fail with references.
///
/// C: int *p = &x;
/// Current: let p: *mut i32 = &mut x as *mut i32; (raw pointer, needs unsafe)
#[test]
fn test_local_pointer_stays_raw_pointer() {
let c_code = r#"
int main() {
int x = 10;
int *p = &x;
*p = 20;
return x;
}
"#;
let result = transpile(c_code).expect("Transpilation should succeed");
println!("Generated Rust code:\n{}", result);
// DECY-118 disabled: Local pointers stay as raw pointers
// This is intentional due to DECY-128 (double pointer compatibility)
assert!(
result.contains("*mut i32") || result.contains("&mut x"),
"Local pointer should be raw pointer or reference\nGenerated:\n{}",
result
);
// Should still have &mut x somewhere (either as reference or cast)
assert!(result.contains("&mut x"), "Should have address-of expression\nGenerated:\n{}", result);
}
/// Test that local pointer to ref compiles.
#[test]
fn test_local_pointer_to_ref_compiles() {
let c_code = r#"
int main() {
int x = 10;
int *p = &x;
*p = 20;
return x;
}
"#;
let result = transpile(c_code).expect("Transpilation should succeed");
println!("Generated Rust code:\n{}", result);
// Try to compile
use std::process::Command;
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let temp_file_path = temp_dir.path().join("test_code.rs");
std::fs::write(&temp_file_path, format!("#![allow(unused)]\n{}", result))
.expect("Failed to write temp file");
let output = Command::new("rustc")
.arg("--emit=metadata")
.arg("--crate-type=lib")
.arg("--crate-name=decy_test")
.arg("--out-dir")
.arg(temp_dir.path())
.arg(&temp_file_path)
.output()
.expect("Failed to run rustc");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"Generated code should compile:\n{}\n\nStderr:\n{}",
result,
stderr
);
}
/// Test pointer initialized with function call (not address-of) stays as-is.
#[test]
fn test_pointer_from_function_stays_pointer() {
// This is a case where we can't easily transform to reference
// because the return type is a pointer
let c_code = r#"
int* get_ptr(int* arr) {
return arr;
}
int main() {
int arr[5];
int *p = get_ptr(arr);
return *p;
}
"#;
let result = transpile(c_code).expect("Transpilation should succeed");
println!("Generated Rust code:\n{}", result);
// Just verify it transpiles - exact type handling depends on context
}