Read a node's eval tail off its record work instead of a class label

This commit is contained in:
Dennis Kobert
2026-09-14 17:20:26 +02:00
parent 0a4e03beb9
commit e6bbfe2ea1
3 changed files with 124 additions and 6 deletions

View File

@@ -16,6 +16,7 @@ pub fn validate_node_fn(parsed: &ParsedNodeFn) -> syn::Result<()> {
validate_record_io,
validate_lazy_reads,
validate_lowering_supported,
validate_opaque_frame_ownership,
];
for validator in validators {
@@ -25,6 +26,21 @@ pub fn validate_node_fn(parsed: &ParsedNodeFn) -> syn::Result<()> {
Ok(())
}
/// A kernel returning `Served` has already closed the frame, so the forward tail
/// it takes emits none of the column work the signature declares. Left to lower,
/// the writes simply never happen and the node reports no error.
fn validate_opaque_frame_ownership(parsed: &ParsedNodeFn) {
let node = crate::codegen::ir::build(parsed);
if crate::codegen::ir::opaque_swallows_columns(&node) {
emit_error!(
parsed.fn_name.span(),
"`{}` serves its own frame, so the attributes it declares would never be written",
parsed.fn_name;
help = "drop the attributes from the return type, or return the element instead of `Served` so the macro owns the frame"
);
}
}
/// A signature no lowering claims generates no `Node` impl at all, so the node
/// compiles and is simply missing at runtime. Naming the gap is the only thing
/// that ends it, since nothing downstream can tell "declined" from "absent".