fix(check): resolve type-scoped borrow-receiver ops in linearity (closes #46)
A function whose parameter is `borrow (RawBuf a)` and which calls RawBuf.get or RawBuf.size on that parameter even once was rejected at `ail check` with [consume-while-borrowed] — the exact borrow-receiver use the ledger advertises for get/size. The diagnostic also misnamed the cause: the receiver is read, not consumed. Root cause: the linearity walk's callee_arg_modes looked up the callee under its spelled name. A type-scoped polymorphic op is spelled `RawBuf.get` at the call site, but check_module_with_visible registers the kernel raw_buf ops under their bare def names (get/size). The lookup missed, the receiver arg defaulted to Consume, and consuming a binder whose borrow_count==1 (the Borrow param) fired the false positive. The kernel signatures themselves are correct (receiver slot is `borrow`); the only defect was the name-resolution gap in the linearity globals lookup. Fix: on a direct-lookup miss, fall back to the bare method name via the existing parse_method_qualifier + qualifier_is_class_shape resolvers, gated on a class/type-shaped qualifier so lowercase cross-module-fn qualifiers (std_list.length) are excluded. This is how the rest of the checker already treats type-scoped polymorphic ops; the fix is general, not RawBuf-specific. This unblocks the natural borrow-helper decomposition (a shared read-only buffer behind a helper fn) — the shape the downstream series milestone's Series.at / Series.total_count need. Surfaced by the raw-buf fieldtest (finding B1, docs/specs/0058). RED-first: rawbuf_borrow_receiver_read_is_linearity_clean.
This commit is contained in:
@@ -708,7 +708,27 @@ impl<'a> Checker<'a> {
|
||||
// with mode info). Look up the global symbol table.
|
||||
let ty = match self.globals.get(name) {
|
||||
Some(t) => t,
|
||||
None => return vec![],
|
||||
None => {
|
||||
// Type-scoped kernel ops (e.g. `RawBuf.get`) are
|
||||
// registered under their BARE def name (`get`) by
|
||||
// `check_module_with_visible`. When the direct lookup
|
||||
// misses and the name is a class/type-shaped qualifier,
|
||||
// retry under the bare method name so the borrow-receiver
|
||||
// modes resolve — same treatment the rest of the checker
|
||||
// gives type-scoped polymorphic ops. A lowercase
|
||||
// cross-module-fn qualifier (`std_list.length`) is NOT
|
||||
// class-shaped and resolves by its own path, so it is
|
||||
// gated out here.
|
||||
let (method_name, qualifier_opt) = crate::parse_method_qualifier(name);
|
||||
if qualifier_opt.is_some() && crate::qualifier_is_class_shape(&qualifier_opt) {
|
||||
match self.globals.get(method_name) {
|
||||
Some(t) => t,
|
||||
None => return vec![],
|
||||
}
|
||||
} else {
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
};
|
||||
let inner = strip_forall(ty);
|
||||
match inner {
|
||||
|
||||
Reference in New Issue
Block a user