Problem
The DEP003/DEP004 over-declaration warning system suppresses when a fn has an opaque external call (i.e., a call to a function not listed in fnNames or moduleEffects). This is correct for truly unknown externals.
But there's an unresolved case: known-signature external functions called optionally via fn?.().
Concrete Case
?bs 0.9
// moduleEffects.json: { "reportService": { "reads": ["network"] } }
fn runner() reads { cache, network } -> void {
cache.update() // reads cache — justified via same-file callee
reportService?.() // optional call to known-effect external
}
Here reportService has a known reads { network } surface via moduleEffects, but the call is optional. Three positions on whether runner should declare reads { network }:
-
Yes, always (conservative) — declared effect surface means "possible effects," not "guaranteed effects." The compiler cannot prove reportService?.() never runs, so the declaration is required. This is the current implicit stance for non-optional calls.
-
No when optional — if the call doesn't execute at runtime, no effect happened. Requires the compiler to track call optionality through the call graph, which adds significant complexity.
-
New syntax — reads? { network } for conditional/optional effects. Precise but adds annotation surface area and creates questions about how conditional effects compose transitively.
Current Behavior
The current hasOpaqueCall implementation (as of the fix in PR #100) treats fn?.() for unknown externals as opaque, suppressing DEP003/DEP004. For known externals (in moduleEffects) called via ?., the behavior is the same as a direct call — the outer fn must declare the full effect surface.
This is position 1 by default, but without it being an explicit design decision.
Questions
- Is position 1 (conservative, always declare) the right default for
?bs 0.9?
- Should optional call detection be extended to the
collectCallees pass so that fn?.() to a known callee is treated differently from fn()?
- Is there a practical use case where position 2 or 3 is meaningfully better?
Relationship to Existing Codes
- DEP001/DEP002 (under-declared reads/writes) — callers of fns with declared effects must propagate those declarations
- DEP003/DEP004 (over-declared reads/writes) — over-declared effects are warned; suppressed for opaque calls
- The optional-call question sits at the intersection: a conditionally-called known external sits between "opaque" (suppress) and "fully tracked" (enforce)
Problem
The DEP003/DEP004 over-declaration warning system suppresses when a fn has an opaque external call (i.e., a call to a function not listed in
fnNamesormoduleEffects). This is correct for truly unknown externals.But there's an unresolved case: known-signature external functions called optionally via
fn?.().Concrete Case
Here
reportServicehas a knownreads { network }surface viamoduleEffects, but the call is optional. Three positions on whetherrunnershould declarereads { network }:Yes, always (conservative) — declared effect surface means "possible effects," not "guaranteed effects." The compiler cannot prove
reportService?.()never runs, so the declaration is required. This is the current implicit stance for non-optional calls.No when optional — if the call doesn't execute at runtime, no effect happened. Requires the compiler to track call optionality through the call graph, which adds significant complexity.
New syntax —
reads? { network }for conditional/optional effects. Precise but adds annotation surface area and creates questions about how conditional effects compose transitively.Current Behavior
The current
hasOpaqueCallimplementation (as of the fix in PR #100) treatsfn?.()for unknown externals as opaque, suppressing DEP003/DEP004. For known externals (inmoduleEffects) called via?., the behavior is the same as a direct call — the outer fn must declare the full effect surface.This is position 1 by default, but without it being an explicit design decision.
Questions
?bs 0.9?collectCalleespass so thatfn?.()to a known callee is treated differently fromfn()?Relationship to Existing Codes