use syn::ext::IdentExt;
use syn::visit::{self, Visit};
use syn::{Expr, ExprCall, ExprMethodCall};
#[derive(Debug, Default, PartialEq, Eq)]
struct HostAccessorCalls {
methods: Vec<&'static str>,
ufcs: Vec<&'static str>,
}
fn host_accessor_name(ident: &syn::Ident) -> Option<&'static str> {
match ident.unraw().to_string().as_str() {
"host_data" => Some("host_data"),
"host_data_mut" => Some("host_data_mut"),
_ => None,
}
}
impl<'ast> Visit<'ast> for HostAccessorCalls {
fn visit_expr_method_call(&mut self, node: &'ast ExprMethodCall) {
if let Some(name) = host_accessor_name(&node.method) {
self.methods.push(name);
}
visit::visit_expr_method_call(self, node);
}
fn visit_expr_call(&mut self, node: &'ast ExprCall) {
if let Expr::Path(path) = node.func.as_ref() {
if let Some(segment) = path.path.segments.last() {
if let Some(name) = host_accessor_name(&segment.ident) {
self.ufcs.push(name);
}
}
}
visit::visit_expr_call(self, node);
}
}
fn host_accessor_calls(path: &str, source: &str) -> HostAccessorCalls {
let file = syn::parse_file(source)
.unwrap_or_else(|error| panic!("failed to parse CUDA source {path}: {error}"));
let mut calls = HostAccessorCalls::default();
calls.visit_file(&file);
calls
}
#[test]
fn plan_execution_stays_on_the_public_raw_session_and_forgets_workspace_on_failed_sync() {
let source = include_str!("../plan.rs");
assert!(!source.contains("cuda::interop"));
assert!(!source.contains("CufftWorkspace"));
assert!(!source.contains("CudaExternalUseReadLease"));
assert!(!source.contains("CudaExternalUseWriteLease"));
assert!(!source.contains("with_typed_device_ptr"));
let execution_section = source
.split_once("fn execute_pair")
.and_then(|(_, rest)| rest.split_once(" pub(crate) fn retained_bytes"))
.map(|(section, _)| section)
.unwrap_or_else(|| unreachable!("execute pair source section should exist"));
assert!(execution_section.contains("with_raw(OP, |raw|"));
assert!(execution_section.contains("bind_plan_to_stream"));
assert!(execution_section.contains("bind_workspace_to_plan"));
assert!(execution_section.contains("raw.tensor(input)"));
assert!(execution_section.contains("raw.tensor_mut(output)"));
assert!(execution_section.contains("raw.alloc_bytes"));
assert!(execution_section.contains("raw.synchronize()"));
assert!(execution_section.contains("raw.retain_tensor(input, OP)"));
assert!(execution_section.contains("raw.retain_tensor(output, OP)"));
assert!(execution_section.contains("std::mem::forget(workspace)"));
assert!(execution_section.contains("std::mem::forget(input_retained)"));
assert!(execution_section.contains("std::mem::forget(output_retained)"));
let workspace_binding_section = source
.split_once("fn bind_workspace_to_plan")
.and_then(|(_, rest)| rest.split_once("/// One cached cuFFT plan"))
.map(|(section, _)| section)
.unwrap_or_else(|| unreachable!("workspace binding source section should exist"));
assert!(workspace_binding_section.contains("cufftSetWorkArea"));
assert!(workspace_binding_section.contains("workspace.with_ptr"));
let forget_block = source
.split_once("if synchronization_error.is_some()")
.map(|(_, rest)| {
let end = rest.find('}').unwrap_or(0);
rest[..end].to_string()
})
.unwrap_or_else(|| unreachable!("synchronization_error guard should exist"));
assert!(
forget_block.contains("std::mem::forget(workspace)")
&& forget_block.contains("std::mem::forget(input_retained)")
&& forget_block.contains("std::mem::forget(output_retained)"),
"failed synchronization must forget workspace and both retained handles"
);
let mod_source = include_str!("../mod.rs");
assert!(!mod_source.contains("cuda::interop"));
assert!(!mod_source.contains("with_typed_device_ptr"));
assert!(!mod_source.contains("ensure_cubecl_resident_typed"));
assert!(mod_source.contains("ensure_gpu_resident(input, OP)"));
assert!(mod_source.contains("with_cubecl"));
assert!(mod_source.contains("alloc_zero_output"));
assert!(mod_source.contains("scale_tensor_write"));
}
#[test]
fn plan_entry_and_session_reject_foreign_runtime_inputs_before_cache_work() {
let mod_source = include_str!("../mod.rs");
let validate_section = mod_source
.split_once("fn validate_cuda_input")
.and_then(|(_, rest)| rest.split_once("fn ensure_cuda_tensor_resident"))
.map(|(section, _)| section)
.unwrap_or_else(|| unreachable!("validate_cuda_input section should exist"));
assert!(validate_section.contains("ensure_cuda_tensor_resident(session, input)?"));
assert!(!validate_section.contains("extension_plan_key_for_runtime"));
assert!(!validate_section.contains("cache.store_mut"));
let validate_call_section = mod_source
.split_once("fn execute_fft")
.and_then(|(_, rest)| rest.split_once("let executed = with_cufft_plan_for_batch"))
.map(|(section, _)| section)
.unwrap_or_else(|| unreachable!("execute_fft head section should exist"));
assert!(
validate_call_section.contains("validate_cuda_input(self, input, spec)?"),
"residency validation must run before the batch-gated plan/cache work"
);
}
#[test]
fn ast_visitor_matches_accessor_calls_without_textual_false_positives() {
let fixture = r#"
fn fixture(tensor: &TypedTensor<f64>, metadata: &Metadata) {
// tensor.host_data(); TypedTensor::<f64>::host_data_mut(&tensor)
let _ = tensor /* whitespace */ . r#host_data :: <f64> ();
let _ = tensor.host_data_mut();
let _ = TypedTensor::<f64>::host_data(&tensor);
let _ = TypedTensor::<f64>::r#host_data_mut(&tensor);
let _ = metadata.host_data_type();
let _ = metadata.host_data_mut_type();
let _ = "tensor.host_data::<f64>(); TypedTensor::<f64>::host_data(&tensor)";
/* tensor.host_data_mut(); TypedTensor::<f64>::host_data(&tensor) */
}
"#;
assert_eq!(
host_accessor_calls("fixture", fixture),
HostAccessorCalls {
methods: vec!["host_data", "host_data_mut"],
ufcs: vec!["host_data", "host_data_mut"],
}
);
}
#[test]
fn cuda_sources_do_not_cross_the_explicit_transfer_boundary() {
let sources = [
("mod.rs", include_str!("../mod.rs")),
("descriptor.rs", include_str!("../descriptor.rs")),
("error.rs", include_str!("../error.rs")),
("ffi.rs", include_str!("../ffi.rs")),
("hermitian.rs", include_str!("../hermitian.rs")),
("plan.rs", include_str!("../plan.rs")),
];
let forbidden_transfers = [
concat!("upload", "_tensor("),
concat!("download", "_tensor("),
];
for (path, source) in sources {
for pattern in forbidden_transfers {
assert!(
!source.contains(pattern),
"CUDA production source {path} must not contain {pattern}"
);
}
assert_eq!(
host_accessor_calls(path, source),
HostAccessorCalls::default(),
"CUDA production source {path} must not call host_data or host_data_mut"
);
}
let cuda_module = include_str!("../mod.rs");
assert!(cuda_module.contains("allocate_cuda_zero_output"));
assert!(!cuda_module.contains("sub(current, current)"));
assert!(!cuda_module.contains("reduce_sum(&zero"));
}
#[test]
fn plan_execution_section_has_no_safe_ffi_pointer_escape() {
let source = include_str!("../plan.rs");
let execution_section = source
.split_once("fn execute_pair")
.and_then(|(_, rest)| rest.split_once(" pub(crate) fn retained_bytes"))
.map(|(section, _)| section)
.unwrap_or_else(|| unreachable!("execute pair source section should exist"));
assert!(!execution_section.contains("unsafe impl Send"));
assert!(!execution_section.contains("unsafe impl Sync"));
}