1use std::collections::HashSet;
2use std::fmt::Write;
3
4use crate::analysis::registry::TypeRegistry;
5use crate::analysis::types::RustNames;
6use crate::ast::stmt::{Stmt, TypeExpr};
7use crate::intern::{Interner, Symbol};
8
9use super::context::{RefinementContext, analyze_variable_capabilities, replace_word};
10use super::detection::{is_result_type, collect_mutable_vars};
11use super::types::codegen_type_expr;
12use super::{
13 CAbiClass, classify_type_for_c_abi,
14 codegen_assertion, codegen_stmt,
15 try_emit_vec_fill_pattern, try_emit_for_range_pattern, try_emit_swap_pattern,
16 try_emit_prefix_reverse,
17};
18
19pub(super) fn is_text_type(ty: &TypeExpr, interner: &Interner) -> bool {
20 match ty {
21 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
22 matches!(interner.resolve(*sym), "Text" | "String")
23 }
24 TypeExpr::Refinement { base, .. } => is_text_type(base, interner),
25 _ => false,
26 }
27}
28
29pub(super) fn is_char_type(ty: &TypeExpr, interner: &Interner) -> bool {
33 match ty {
34 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
35 interner.resolve(*sym) == "Char"
36 }
37 TypeExpr::Refinement { base, .. } => is_char_type(base, interner),
38 _ => false,
39 }
40}
41
42pub(super) fn map_type_to_c_abi(ty: &TypeExpr, interner: &Interner, is_return: bool) -> String {
45 if is_text_type(ty, interner) {
46 if is_return {
47 "*mut std::os::raw::c_char".to_string()
48 } else {
49 "*const std::os::raw::c_char".to_string()
50 }
51 } else {
52 codegen_type_expr(ty, interner)
53 }
54}
55
56pub(super) fn is_native_scalar(ty: &TypeExpr, interner: &Interner) -> bool {
61 match ty {
62 TypeExpr::Primitive(sym) | TypeExpr::Named(sym) => {
63 matches!(interner.resolve(*sym), "Int" | "Float" | "Bool")
64 }
65 TypeExpr::Refinement { base, .. } => is_native_scalar(base, interner),
66 _ => false,
67 }
68}
69
70pub fn codegen_native_tier_export(
80 name: Symbol,
81 params: &[(Symbol, &TypeExpr)],
82 return_type: Option<&TypeExpr>,
83 interner: &Interner,
84) -> Option<String> {
85 if !params.iter().all(|(_, t)| is_native_scalar(t, interner)) {
86 return None;
87 }
88 if let Some(rt) = return_type {
89 if !is_native_scalar(rt, interner) {
90 return None;
91 }
92 }
93 let names = RustNames::new(interner);
94 let inner = names.ident(name);
95 let export = format!("logos_native_{}", names.raw(name));
96 let params_sig = params
97 .iter()
98 .map(|(p, t)| format!("{}: {}", interner.resolve(*p), codegen_type_expr(t, interner)))
99 .collect::<Vec<_>>()
100 .join(", ");
101 let args = params
102 .iter()
103 .map(|(p, _)| interner.resolve(*p).to_string())
104 .collect::<Vec<_>>()
105 .join(", ");
106 let ret = return_type
107 .map(|t| codegen_type_expr(t, interner))
108 .unwrap_or_else(|| "()".to_string());
109 let ret_sig = if ret == "()" { String::new() } else { format!(" -> {ret}") };
110 let mut out = String::new();
111 let _ = writeln!(out, "#[no_mangle]");
112 let _ = writeln!(out, "pub extern \"C\" fn {export}({params_sig}){ret_sig} {{");
113 let _ = writeln!(out, " {inner}({args})");
114 let _ = writeln!(out, "}}");
115 Some(out)
116}
117
118pub(super) fn codegen_c_export_with_marshaling(
128 name: Symbol,
129 params: &[(Symbol, &TypeExpr)],
130 body: &[Stmt],
131 return_type: Option<&TypeExpr>,
132 interner: &Interner,
133 lww_fields: &HashSet<(String, String)>,
134 mv_fields: &HashSet<(String, String)>,
135 async_functions: &HashSet<Symbol>,
136 boxed_fields: &HashSet<(String, String, String)>,
137 registry: &crate::analysis::registry::TypeRegistry,
138 type_env: &crate::analysis::types::TypeEnv,
139) -> String {
140 let mut output = String::new();
141 let names = RustNames::new(interner);
142 let raw_name = names.raw(name);
143 let func_name = format!("logos_{}", raw_name);
147 let inner_name = names.ident(name);
148
149 let has_ref_return = return_type.map_or(false, |ty| {
151 classify_type_for_c_abi(ty, interner, registry) == CAbiClass::ReferenceType
152 });
153 let has_result_return = return_type.map_or(false, |ty| is_result_type(ty, interner));
154 let has_text_return = return_type.map_or(false, |t| is_text_type(t, interner));
155
156 let uses_status_code = has_ref_return || has_result_return || has_text_return
161 || params.iter().any(|(_, ty)| matches!(ty, TypeExpr::Refinement { .. }));
162
163 let inner_params: Vec<String> = params.iter()
165 .map(|(pname, ptype)| {
166 format!("{}: {}", interner.resolve(*pname), codegen_type_expr(ptype, interner))
167 })
168 .collect();
169 let inner_ret = return_type.map(|t| codegen_type_expr(t, interner));
170
171 let inner_sig = if let Some(ref ret) = inner_ret {
172 if ret != "()" {
173 format!("fn {}({}) -> {}", inner_name, inner_params.join(", "), ret)
174 } else {
175 format!("fn {}({})", inner_name, inner_params.join(", "))
176 }
177 } else {
178 format!("fn {}({})", inner_name, inner_params.join(", "))
179 };
180
181 writeln!(output, "{} {{", inner_sig).unwrap();
182 let func_mutable_vars = collect_mutable_vars(body);
183 let mut func_ctx = RefinementContext::new();
184 let mut func_synced_vars = HashSet::new();
185 let func_var_caps = analyze_variable_capabilities(body, interner);
186 for (param_name, param_type) in params {
187 let type_name = codegen_type_expr(param_type, interner);
188 func_ctx.register_variable_type(*param_name, type_name);
189 }
190 let func_pipe_vars = HashSet::new();
191 {
192 let stmt_refs: Vec<&Stmt> = body.iter().collect();
193 let mut si = 0;
194 while si < stmt_refs.len() {
195 if let Some((code, skip)) = try_emit_vec_fill_pattern(&stmt_refs, si, interner, 1, &mut func_ctx) {
196 output.push_str(&code);
197 si += 1 + skip;
198 continue;
199 }
200 if let Some((code, skip)) = try_emit_for_range_pattern(&stmt_refs, si, interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env) {
201 output.push_str(&code);
202 si += 1 + skip;
203 continue;
204 }
205 if let Some((code, skip)) = try_emit_prefix_reverse(&stmt_refs, si, interner, 1, func_ctx.get_variable_types()) {
206 output.push_str(&code);
207 si += 1 + skip;
208 continue;
209 }
210 if let Some((code, skip)) = try_emit_swap_pattern(&stmt_refs, si, interner, 1, func_ctx.get_variable_types(), func_ctx.oracle()) {
211 output.push_str(&code);
212 si += 1 + skip;
213 continue;
214 }
215 output.push_str(&codegen_stmt(stmt_refs[si], interner, 1, &func_mutable_vars, &mut func_ctx, lww_fields, mv_fields, &mut func_synced_vars, &func_var_caps, async_functions, &func_pipe_vars, boxed_fields, registry, type_env));
216 si += 1;
217 }
218 }
219 writeln!(output, "}}\n").unwrap();
220
221 let mut c_params: Vec<String> = Vec::new();
223
224 for (pname, ptype) in params.iter() {
225 let pn = names.ident(*pname);
226 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
227 c_params.push(format!("{}: LogosHandle", pn));
228 } else if is_text_type(ptype, interner) {
229 c_params.push(format!("{}: *const std::os::raw::c_char", pn));
230 } else if is_char_type(ptype, interner) {
231 c_params.push(format!("{}: u32", pn));
233 } else {
234 c_params.push(format!("{}: {}", pn, codegen_type_expr(ptype, interner)));
235 }
236 }
237
238 if uses_status_code {
240 if let Some(ret_ty) = return_type {
241 if has_result_return {
242 if let TypeExpr::Generic { params: ref rparams, .. } = ret_ty {
244 if !rparams.is_empty() {
245 let ok_ty = &rparams[0];
246 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
247 c_params.push("out: *mut LogosHandle".to_string());
248 } else if is_text_type(ok_ty, interner) {
249 c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
250 } else {
251 let ty_str = codegen_type_expr(ok_ty, interner);
252 c_params.push(format!("out: *mut {}", ty_str));
253 }
254 }
255 }
256 } else if has_ref_return {
257 c_params.push("out: *mut LogosHandle".to_string());
258 } else if has_text_return {
259 c_params.push("out: *mut *mut std::os::raw::c_char".to_string());
260 }
261 }
262 }
263
264 let c_sig = if uses_status_code {
266 format!("pub extern \"C\" fn {}({}) -> LogosStatus", func_name, c_params.join(", "))
267 } else if has_text_return {
268 format!("pub extern \"C\" fn {}({}) -> *mut std::os::raw::c_char", func_name, c_params.join(", "))
269 } else if let Some(ret_ty) = return_type {
270 let ret_str = if is_char_type(ret_ty, interner) {
272 "u32".to_string()
273 } else {
274 codegen_type_expr(ret_ty, interner)
275 };
276 if ret_str != "()" {
277 format!("pub extern \"C\" fn {}({}) -> {}", func_name, c_params.join(", "), ret_str)
278 } else {
279 format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
280 }
281 } else {
282 format!("pub extern \"C\" fn {}({})", func_name, c_params.join(", "))
283 };
284
285 writeln!(output, "#[no_mangle]").unwrap();
286 writeln!(output, "{} {{", c_sig).unwrap();
287
288 let call_args: Vec<String> = params.iter()
290 .map(|(pname, ptype)| {
291 let pname_str = names.ident(*pname);
292 if classify_type_for_c_abi(ptype, interner, registry) == CAbiClass::ReferenceType {
293 let rust_ty = codegen_type_expr(ptype, interner);
299 let err_return = if uses_status_code {
300 "return LogosStatus::InvalidHandle;".to_string()
301 } else if return_type.map_or(false, |t| codegen_type_expr(t, interner) != "()") {
302 "return Default::default();".to_string()
303 } else {
304 "return;".to_string()
305 };
306 writeln!(output, " let {pn} = {{", pn = pname_str).unwrap();
307 writeln!(output, " let __id = {pn} as u64;", pn = pname_str).unwrap();
308 writeln!(output, " let __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
309 writeln!(output, " match __reg.deref(__id) {{").unwrap();
310 writeln!(output, " Some(__ptr) => {{ let __v = unsafe {{ &*(__ptr as *const {ty}) }}.clone(); drop(__reg); __v }}", ty = rust_ty).unwrap();
311 writeln!(output, " None => {{ drop(__reg); logos_set_last_error(\"InvalidHandle: handle '{pn}' not found in registry\".to_string()); {ret} }}", pn = pname_str, ret = err_return).unwrap();
312 writeln!(output, " }}").unwrap();
313 writeln!(output, " }};").unwrap();
314 } else if is_text_type(ptype, interner) {
315 if uses_status_code {
317 writeln!(output, " if {pn}.is_null() {{ logos_set_last_error(\"NullPointer: text parameter '{pn}' is null\".to_string()); return LogosStatus::NullPointer; }}",
318 pn = pname_str).unwrap();
319 writeln!(output, " let {pn} = unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }};",
320 pn = pname_str).unwrap();
321 } else {
322 writeln!(output, " let {pn} = if {pn}.is_null() {{ String::new() }} else {{ unsafe {{ std::ffi::CStr::from_ptr({pn}).to_string_lossy().into_owned() }} }};",
324 pn = pname_str).unwrap();
325 }
326 } else if is_char_type(ptype, interner) {
327 writeln!(output, " let {pn} = char::from_u32({pn}).unwrap_or('\\u{{FFFD}}');",
330 pn = pname_str).unwrap();
331 }
332 pname_str.to_string()
333 })
334 .collect();
335
336 for (pname, ptype) in params.iter() {
338 if let TypeExpr::Refinement { base: _, var, predicate } = ptype {
339 let pname_str = interner.resolve(*pname);
340 let bound = interner.resolve(*var);
341 let assertion = codegen_assertion(predicate, interner);
342 let check = if bound == pname_str {
343 assertion
344 } else {
345 replace_word(&assertion, bound, pname_str)
346 };
347 writeln!(output, " if !({}) {{", check).unwrap();
348 writeln!(output, " logos_set_last_error(format!(\"Refinement violation: expected {check}, got {pn} = {{}}\", {pn}));",
349 check = check, pn = pname_str).unwrap();
350 writeln!(output, " return LogosStatus::RefinementViolation;").unwrap();
351 writeln!(output, " }}").unwrap();
352 }
353 }
354
355 if uses_status_code && (has_ref_return || has_text_return || has_result_return) {
357 writeln!(output, " if out.is_null() {{ logos_set_last_error(\"NullPointer: output parameter is null\".to_string()); return LogosStatus::NullPointer; }}").unwrap();
358 }
359
360 let panic_default = if uses_status_code {
362 "LogosStatus::ThreadPanic"
363 } else if has_text_return {
364 "std::ptr::null_mut()"
365 } else if return_type.map_or(false, |t| codegen_type_expr(t, interner) != "()") {
366 "Default::default()"
367 } else {
368 "" };
370
371 writeln!(output, " match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {{").unwrap();
373
374 if uses_status_code {
376 if has_result_return {
377 writeln!(output, " match {}({}) {{", inner_name, call_args.join(", ")).unwrap();
379 writeln!(output, " Ok(val) => {{").unwrap();
380
381 if let Some(TypeExpr::Generic { params: ref rparams, .. }) = return_type {
382 if !rparams.is_empty() {
383 let ok_ty = &rparams[0];
384 if classify_type_for_c_abi(ok_ty, interner, registry) == CAbiClass::ReferenceType {
385 writeln!(output, " let __ptr = Box::into_raw(Box::new(val)) as usize;").unwrap();
386 writeln!(output, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
387 writeln!(output, " let (__id, _) = __reg.register(__ptr);").unwrap();
388 writeln!(output, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
389 } else if is_text_type(ok_ty, interner) {
390 writeln!(output, " match std::ffi::CString::new(val) {{").unwrap();
391 writeln!(output, " Ok(cstr) => unsafe {{ *out = cstr.into_raw(); }},").unwrap();
392 writeln!(output, " Err(_) => {{").unwrap();
393 writeln!(output, " logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
394 writeln!(output, " return LogosStatus::ContainsNullByte;").unwrap();
395 writeln!(output, " }}").unwrap();
396 writeln!(output, " }}").unwrap();
397 } else {
398 writeln!(output, " unsafe {{ *out = val; }}").unwrap();
399 }
400 }
401 }
402
403 writeln!(output, " LogosStatus::Ok").unwrap();
404 writeln!(output, " }}").unwrap();
405 writeln!(output, " Err(e) => {{").unwrap();
406 writeln!(output, " logos_set_last_error(format!(\"{{}}\", e));").unwrap();
407 writeln!(output, " LogosStatus::Error").unwrap();
408 writeln!(output, " }}").unwrap();
409 writeln!(output, " }}").unwrap();
410 } else if has_ref_return {
411 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
413 writeln!(output, " let __ptr = Box::into_raw(Box::new(result)) as usize;").unwrap();
414 writeln!(output, " let mut __reg = logos_handle_registry().lock().unwrap_or_else(|e| e.into_inner());").unwrap();
415 writeln!(output, " let (__id, _) = __reg.register(__ptr);").unwrap();
416 writeln!(output, " unsafe {{ *out = __id as LogosHandle; }}").unwrap();
417 writeln!(output, " LogosStatus::Ok").unwrap();
418 } else if has_text_return {
419 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
421 writeln!(output, " match std::ffi::CString::new(result) {{").unwrap();
422 writeln!(output, " Ok(cstr) => {{").unwrap();
423 writeln!(output, " unsafe {{ *out = cstr.into_raw(); }}").unwrap();
424 writeln!(output, " LogosStatus::Ok").unwrap();
425 writeln!(output, " }}").unwrap();
426 writeln!(output, " Err(_) => {{").unwrap();
427 writeln!(output, " logos_set_last_error(\"Return value contains null byte\".to_string());").unwrap();
428 writeln!(output, " LogosStatus::ContainsNullByte").unwrap();
429 writeln!(output, " }}").unwrap();
430 writeln!(output, " }}").unwrap();
431 } else {
432 writeln!(output, " {}({});", inner_name, call_args.join(", ")).unwrap();
434 writeln!(output, " LogosStatus::Ok").unwrap();
435 }
436 } else if has_text_return {
437 writeln!(output, " let result = {}({});", inner_name, call_args.join(", ")).unwrap();
439 writeln!(output, " match std::ffi::CString::new(result) {{").unwrap();
440 writeln!(output, " Ok(cstr) => cstr.into_raw(),").unwrap();
441 writeln!(output, " Err(_) => {{ logos_set_last_error(\"Return value contains null byte\".to_string()); std::ptr::null_mut() }}").unwrap();
442 writeln!(output, " }}").unwrap();
443 } else if let Some(ret_ty) = return_type {
444 if is_char_type(ret_ty, interner) {
446 writeln!(output, " {}({}) as u32", inner_name, call_args.join(", ")).unwrap();
447 } else {
448 writeln!(output, " {}({})", inner_name, call_args.join(", ")).unwrap();
449 }
450 } else {
451 writeln!(output, " {}({})", inner_name, call_args.join(", ")).unwrap();
452 }
453
454 writeln!(output, " }})) {{").unwrap();
456 writeln!(output, " Ok(__v) => __v,").unwrap();
457 writeln!(output, " Err(__panic) => {{").unwrap();
458 writeln!(output, " let __msg = if let Some(s) = __panic.downcast_ref::<String>() {{ s.clone() }} else if let Some(s) = __panic.downcast_ref::<&str>() {{ s.to_string() }} else {{ \"Unknown panic\".to_string() }};").unwrap();
459 writeln!(output, " logos_set_last_error(__msg);").unwrap();
460 if !panic_default.is_empty() {
461 writeln!(output, " {}", panic_default).unwrap();
462 }
463 writeln!(output, " }}").unwrap();
464 writeln!(output, " }}").unwrap();
465
466 writeln!(output, "}}\n").unwrap();
467
468 output
469}