@@ -371,6 +371,12 @@ installed_function(val, m) :- expr_kind(_, "ASSIGNMENT", _, val), expr_child(_,
371371// filtering by nesting is needed. Plus `@returns {T}`.
372372return_value(m, k, i) :- expr_root(_, "RETURN", e), expr_owner(_, m, _, e), !expr_kind(_, "THIS", _, e),
373373 !wrapped_result_return(e), expr_value(e, k, i).
374+ // A wrapper closure's carrying return keeps every value that does NOT come from the
375+ // parameter's call: `fn() || fallback`, `override || (cached = make())`, a variable
376+ // that starts as a default (below). Not an `undefined` placeholder (`let result =
377+ // undefined`): that is the cache before its fill, never what a call returns.
378+ return_value(m, k, i) :- expr_root(_, "RETURN", e), expr_owner(_, m, _, e), wrapped_result_return(e),
379+ carry_rest(e, x), expr_value(x, k, i), (k != "ambient" ; i != "undefined").
374380return_value(m, "inst", t) :- method_return_type(_, _, tr, m), type_ref_type(tr, t).
375381
376382// ── a WRAPPER's closure returns what its argument returns, per call site (#695) ──
@@ -394,6 +400,9 @@ return_value(m, "inst", t) :- method_return_type(_, _, tr, m), type_ref_type(tr,
394400wrapper_closure(h, c) :- expr_root(_, "RETURN", e), expr_owner(_, h, _, e), expr_introduces(_, c, e).
395401wrapper_closure(h, c) :- expr_root(_, "RETURN", e), expr_owner(_, h, _, e), expr_binding(_, v, e),
396402 var_init(_, _, init, v), expr_introduces(_, c, init).
403+ // ... or names a function declared inside the wrapper: `function inner() { ... } return inner`.
404+ wrapper_closure(h, c) :- expr_root(_, "RETURN", e), expr_owner(_, h, _, e), expr_binding(_, v, e),
405+ function_binding_method(v, c), method_enclosing(c, h).
397406param_call_kind("FUNCTION_CALL").
398407param_call_kind("FUNCTION_CALL_CALL").
399408param_call_kind("FUNCTION_CALL_APPLY").
@@ -413,7 +422,7 @@ var_carries_param_call(v, p) :- expr_kind(_, "ASSIGNMENT", _, a), expr_operator(
413422// the wrapper's parameter at that position.
414423wrapper_forwards(h, c, pos) :- wrapper_closure(h, c), param_decl(_, _, ppos, h, p), !param_is_rest(_, p),
415424 pos = to_number(ppos), expr_root(_, "RETURN", e), expr_owner(_, c, _, e), carries_param_call(e, p).
416- wrapped_result_return(e) :- wrapper_closure(h, c), param_decl(_, _, _, h, p),
425+ wrapped_result_return(e) :- wrapper_closure(h, c), param_decl(_, _, _, h, p), !param_is_rest(_, p),
417426 expr_root(_, "RETURN", e), expr_owner(_, c, _, e), carries_param_call(e, p).
418427// ── the wrapper WRITES that parameter before the closure calls it (#1706) ──
419428// `fn = () => x`, `if (!fn) fn = dflt`, `fn = fn || dflt`, `fn ??= dflt` in the
@@ -456,16 +465,53 @@ block_kind_branches("TRY").
456465block_kind_branches("CATCH").
457466wrapper_param_replaced(h, pos) :- wrapper_param_write(h, pos, _, a), !param_write_keeps_arg(a).
458467wrapper_param_written(h, pos, o) :- wrapper_param_write(h, pos, p, a), param_write_leaf(a, o), !expr_param(_, p, o).
468+ // Only the parameter's call is answered per site. carry_part walks the carrying
469+ // return down the same operators as carries_param_call; carry_rest is each operand
470+ // on the way that carries no parameter call (a fallback, an override, a variable's
471+ // default initializer), which keeps its ordinary value.
472+ carry_part(e, e) :- wrapped_result_return(e).
473+ carry_part(e, c) :- carry_part(e, x), carries_param_call(x, _), carry_step(x, c).
474+ carry_step(x, c) :- expr_kind(_, "CONDITIONAL", _, x), expr_child(_, x, r, _, c), r != "CONDITION".
475+ carry_step(x, c) :- expr_kind(_, "BINARY", _, x), expr_operator(_, op, x), binary_op_passes_value(op), expr_child(_, x, _, _, c).
476+ carry_step(x, c) :- expr_kind(_, "ASSIGNMENT", _, x), expr_child(_, x, "ASSIGNMENT_VALUE", _, c).
477+ carry_step(x, c) :- expr_kind(_, "SEQUENCE", _, x), sequence_last(x, c).
478+ carry_step(x, c) :- expr_binding(_, v, x), var_init(_, _, c, v).
479+ carry_step(x, c) :- expr_binding(_, v, x), expr_kind(_, "ASSIGNMENT", _, a), expr_operator(_, op, a), assignment_op_writes_value(op),
480+ expr_child(_, a, "ASSIGNMENT_TARGET", _, tgt), expr_binding(_, v, tgt), expr_child(_, a, "ASSIGNMENT_VALUE", _, c).
481+ carry_rest(e, x) :- carry_part(e, x), !carries_param_call(x, _).
482+ // What the carrying return holds read context-insensitively: every caller's argument
483+ // merged (and nothing once the wrapper is over the fan cap). Only the fallbacks below
484+ // use it, where no one site's argument says what the parameter is.
485+ carried_value(c, k, i) :- expr_root(_, "RETURN", e), expr_owner(_, c, _, e), wrapped_result_return(e), expr_value(e, k, i).
459486// The site's own value, and a call through it.
460487expr_value(s, "wrap", s) :- expr_kind(_, "CALL", _, s), expr_resolves_to_method(s, h), wrapper_forwards(h, _, _).
461488expr_value(ce, k, i) :- expr_kind(_, "CALL", _, ce), callee_value(ce, "wrap", s),
489+ !call_site(_, "FUNCTION_CALL_BIND", _, _, _, _, ce, _, _), wrap_call_value(s, k, i).
490+ // wrap_site(Site, Wrapper, Position); wrap_site_arg when the site passes that position
491+ // itself (no spread at or before it), so its argument alone is the parameter.
492+ wrap_site(s, h, pos) :- expr_kind(_, "CALL", _, s), expr_resolves_to_method(s, h), wrapper_forwards(h, _, pos).
493+ wrap_site_spread(s, pos) :- wrapper_forwards(_, _, pos), call_arg(s, p2, a), p2 <= pos, expr_kind(_, "SPREAD", _, a).
494+ wrap_site_arg(s, pos, arg) :- wrap_site(s, h, pos), call_arg(s, pos, arg), !wrap_site_spread(s, pos), !wrapper_param_replaced(h, pos).
495+ // The argument is a function: what it returns. The argument is itself a wrapped
496+ // getter (`memoize(memoize(f))`, `once(getAlpha)`): what a call through that site returns.
497+ wrap_call_value(s, k, i) :- wrap_site_arg(s, _, arg), expr_value(arg, "func", g), return_value(g, k, i).
498+ wrap_call_value(s, k, i) :- wrap_site_arg(s, _, arg), expr_value(arg, "wrap", s2), wrap_call_value(s2, k, i).
499+ // No argument at that position: the parameter's default, `lazy(make = makeDefault)`.
500+ wrap_call_value(s, k, i) :- wrap_site(s, h, pos), !call_arg(s, pos, _), !wrap_site_spread(s, pos), !wrapper_param_replaced(h, pos),
501+ param_decl(_, _, ppos, h, p), to_number(ppos) = pos, param_default_text(_, txt, h, p),
502+ expr_root(_, "PARAMETER_DEFAULT", d), expr_owner(_, h, _, d), expr_text(_, txt, d),
503+ expr_value(d, "func", g), return_value(g, k, i).
504+ // A spread argument: the site cannot say which function lands in the parameter.
505+ wrap_call_value(s, k, i) :- wrap_site_spread(s, pos), wrap_site(s, h, pos), !wrapper_param_replaced(h, pos), wrapper_forwards(h, c, pos), carried_value(c, k, i).
506+ // What the wrapper itself writes into the parameter (#1706) is called at every site, whatever it passes.
507+ wrap_call_value(s, k, i) :- wrap_site(s, h, pos), wrapper_param_written(h, pos, o), expr_value(o, "func", g), return_value(g, k, i).
508+ // The closure called inside the wrapper itself (`const c = () => fn(); c(); return c`)
509+ // is not reached through a site: there the parameter is this invocation's, read merged.
510+ expr_value(ce, k, i) :- expr_kind(_, "CALL", _, ce), expr_resolves_to_method(ce, c), wrapper_forwards(h, c, _),
462511 !call_site(_, "FUNCTION_CALL_BIND", _, _, _, _, ce, _, _),
463- expr_resolves_to_method(s, h), wrapper_forwards(h, _, pos), !wrapper_param_replaced(h, pos), call_arg(s, pos, arg),
464- expr_value(arg, "func", g), return_value(g, k, i).
465- expr_value(ce, k, i) :- expr_kind(_, "CALL", _, ce), callee_value(ce, "wrap", s),
466- !call_site(_, "FUNCTION_CALL_BIND", _, _, _, _, ce, _, _),
467- expr_resolves_to_method(s, h), wrapper_forwards(h, _, pos), wrapper_param_written(h, pos, o),
468- expr_value(o, "func", g), return_value(g, k, i).
512+ expr_owner(_, m, _, ce), within_wrapper(m, h), carried_value(c, k, i).
513+ within_wrapper(h, h) :- wrapper_forwards(h, _, _).
514+ within_wrapper(m, h) :- method_enclosing(m, e), within_wrapper(e, h).
469515// `return this` is POLYMORPHIC: a fluent `add()` declared on Node and called on an
470516// AbstractBlock returns the AbstractBlock. So a `this`-return is not a value of the
471517// method — it is the RECEIVER of each call, substituted at the site (below). A
0 commit comments