Summary
BDFL ruling #16 changed the spec: c_import may make C resource cleanup ergonomic only when ownership is proven or explicitly asserted. Name heuristics alone may suggest candidates, but they must not insert cleanup, call destructors, generate owning wrappers, or mark raw C values as owned.
The current compiler still implements heuristic-only C destructor auto-defer.
Current behavior / suspected code paths
The current implementation has a complete name-heuristic auto-defer pipeline:
src/Sema.w:607-609
ci_type_destructors / ci_auto_defer_bindings fields for c_import auto-defer
src/SemaDecl.w:457-480
build_ci_destructor_map scans c_import-origin method names and records
`.free`, `.destroy`, `.close`, `.unref`, `.release` as destructors
src/SemaCheck.w:4416-4432
immutable let binding of a c_import pointer/struct type records an auto-defer
destructor binding when the type has a name-matched destructor
src/MirLower.w:472-500
emit_auto_defers lowers the recorded destructor call at scope exit
This is exactly the rejected rule: method-name detection becomes ownership and cleanup.
docs/completed/c-import-auto-methods.md also documents the old design as if auto-defer were the intended behavior; update/archive notes so future agents do not treat that completed design as current policy.
Expected behavior
c_import resource cleanup must use a two-step model:
-
Establish ownership only from evidence that proves or asserts the contract:
- explicit annotation;
- author-supplied or imported metadata;
- conservative source/header analysis strong enough to prove ownership;
- curated library-specific conventions for known libraries;
- hand-written owning wrappers.
-
Express cleanup only through an owning wrapper type whose Drop calls the correct C destructor.
Name heuristics may produce candidates, import-manifest notes, or diagnostics suggesting a likely constructor/destructor pairing. They may not, by themselves:
- insert cleanup;
- call a destructor;
- generate an owning wrapper;
- mark a raw pointer/handle as owned.
Raw pointers and raw handles stay raw unless wrapped by a proven ownership model.
Reference-counted resources require ownership-specific modeling: Drop as unref is valid only for values built from an owning +1 constructor/retain/copy/create operation. Borrowing accessors produce non-owning handles with no Drop.
Root cause / five whys
- Why can the compiler call a C destructor the user did not request? Because
ci_type_destructors records destructor-looking names and ci_auto_defer_bindings records cleanup for matching let bindings.
- Why are names enough? The old spec said
prefix_destroy, prefix_free, prefix_close, prefix_unref, and prefix_release are destructors and allowed auto-defer for non-escaping let bindings.
- Why is that unsafe? C names do not prove ownership. Constructor-looking functions may return borrowed, singleton, arena-owned, retained, or otherwise non-owned handles; destructor-looking functions can have preconditions or apply only to specific ownership states.
- Why is scope-local auto-defer the wrong mechanism even with ownership? It ties cleanup to lexical scope instead of value lifetime and does not compose with moves, returns, or storage. The old escape suppression admits this: the compiler simply stops helping when the resource is moved/stored.
- Deepest cause: the importer conflates naming, ownership evidence, and cleanup mechanism. With needs a modeled owned value (
Drop wrapper) for safe cleanup, not a destructor call bolted onto a raw C pointer by heuristic.
Acceptance criteria
- Remove or disable heuristic
ci_type_destructors / ci_auto_defer_bindings cleanup insertion.
build_ci_destructor_map no longer turns generic method names into ownership facts.
SemaCheck no longer records auto-defer bindings based only on c_import type + destructor-looking method.
MirLower no longer emits c_import heuristic auto-defers.
- Add a regression showing a
*_new + *_destroy pair does not automatically call destroy solely from names.
- Add tests or design hooks for candidate metadata/diagnostics, without automatic cleanup.
- Design and implement the future positive path separately: proven ownership metadata/annotation/curated convention/hand wrapper generates an owning
Drop wrapper.
- Reference-counted APIs distinguish owning +1 constructors/retains from borrowed accessors.
- Update stale completed-design docs that still describe auto-defer as current behavior.
Spec references
docs/with-specification.md §16.2a Auto-Method Generation / Proven ownership cleanup
docs/requirements.md 16.2.2.11 through 16.2.2.27
Summary
BDFL ruling #16 changed the spec:
c_importmay make C resource cleanup ergonomic only when ownership is proven or explicitly asserted. Name heuristics alone may suggest candidates, but they must not insert cleanup, call destructors, generate owning wrappers, or mark raw C values as owned.The current compiler still implements heuristic-only C destructor auto-defer.
Current behavior / suspected code paths
The current implementation has a complete name-heuristic auto-defer pipeline:
This is exactly the rejected rule: method-name detection becomes ownership and cleanup.
docs/completed/c-import-auto-methods.mdalso documents the old design as if auto-defer were the intended behavior; update/archive notes so future agents do not treat that completed design as current policy.Expected behavior
c_importresource cleanup must use a two-step model:Establish ownership only from evidence that proves or asserts the contract:
Express cleanup only through an owning wrapper type whose
Dropcalls the correct C destructor.Name heuristics may produce candidates, import-manifest notes, or diagnostics suggesting a likely constructor/destructor pairing. They may not, by themselves:
Raw pointers and raw handles stay raw unless wrapped by a proven ownership model.
Reference-counted resources require ownership-specific modeling:
Dropasunrefis valid only for values built from an owning +1 constructor/retain/copy/create operation. Borrowing accessors produce non-owning handles with noDrop.Root cause / five whys
ci_type_destructorsrecords destructor-looking names andci_auto_defer_bindingsrecords cleanup for matchingletbindings.prefix_destroy,prefix_free,prefix_close,prefix_unref, andprefix_releaseare destructors and allowed auto-defer for non-escapingletbindings.Dropwrapper) for safe cleanup, not a destructor call bolted onto a raw C pointer by heuristic.Acceptance criteria
ci_type_destructors/ci_auto_defer_bindingscleanup insertion.build_ci_destructor_mapno longer turns generic method names into ownership facts.SemaCheckno longer records auto-defer bindings based only on c_import type + destructor-looking method.MirLowerno longer emits c_import heuristic auto-defers.*_new+*_destroypair does not automatically call destroy solely from names.Dropwrapper.Spec references
docs/with-specification.md§16.2a Auto-Method Generation / Proven ownership cleanupdocs/requirements.md16.2.2.11through16.2.2.27