Skip to content

#548 fix is incomplete: <action> of <args> to an include-exposed action is still context-dependently fatal (works in main loop/tests, fatal at top level & in action bodies) #580

Description

@logbie

Summary

This is the same bug as #548, surfacing through a call form that #548's fix did not cover. #548 described a context-dependent fatal Undefined/not defined for include-exposed actions and fixed it for the call <action> with <args> form (now non-fatal everywhere). But the idiomatic <action> of <args> form still shows the original, pre-#548 context-dependent behavior: it resolves fine inside a main loop or a describe/test block, but is fatal at top level and inside an action body.

Because of is the natural WFL call syntax, this keeps multi-file libraries unusable through their natural top-level API even though #548 is marked completed.

Environment

  • WFL built from source today, .build_meta.json: { "year": 26, "month": 7, "build": 14 } (current main)
  • Linux, release build (cargo build --release)

The matrix (measured on 26.7.14)

mod.wfl:

define action called greet with parameters s:
    return "HI-" with s
end action
call form top-level stmt inside define action body inside main loop inside describe/test
call greet with "bob" ✅ works ✅ works ✅ works ✅ works
greet of "bob" fatal fatal ✅ works ✅ works

The call … with row is exactly #548's fix. The of row is #548's original symptom table (top-level fatal, main loop/test non-fatal) — it was never given the top-level relaxation. The "action body → fatal" cell is an additional context #548's table didn't enumerate.

Repro for the still-fatal cell

main.wfl:

include from "mod.wfl"
display "BEFORE"
store g as greet of "bob"
display "AFTER=" with g

wfl main.wfl

error[ANALYZE-SEMANTIC]: Variable 'greet' is not defined

BEFORE never prints; exit 3. Swapping only that line to store g as call greet with "bob" prints BEFORE / AFTER=HI-bob on the same build.

Why the two forms diverge (pointers)

  • greet of "bob" parses to Expression::FunctionCall { function: Variable("greet"), .. }. The analyzer analyzes the callee first via analyze_expression(function) (src/analyzer/mod.rs:2247), which hits the Expression::Variable branch (~mod.rs:2223) and calls report_undefined_name (src/analyzer/mod.rs:482) — fatal unless try_depth > 0, and it does not consult has_includes.
  • The has_includes relaxation Calling an include-exposed action from top level is a fatal 'Undefined action' (works inside main loop / test blocks) #548 added lives only in the ActionCall path at src/analyzer/mod.rs:2533. The FunctionCallVariable callee path (what of produces) never reaches it.
  • Severity::Error is emitted at src/analyzer/static_analyzer.rs:175; src/main.rs:1109-1119 does process::exit(3) on any error-severity semantic diagnostic. (Inside main loop/test, the same reference is emitted as a non-fatal warning instead — hence the context-dependence.)

Suggested fix

Extend #548's relaxation to the FunctionCall callee path: when a FunctionCall's callee is an unresolved Variable, has_includes is true, and it's not a builtin/param, route it through the same non-fatal-warning path as mod.rs:2533 instead of report_undefined_name's fatal branch — so <name> of <args> behaves like call <name> with <args> under include from.

Related, same class as #547: nested includes

An included file that references an action from a file it includes is fatal during the included file's own (isolated) analysis:

// base.wfl
define action called base_op with parameters x:
    return "base(" with x with ")"
end action

// mid.wfl
include from "base.wfl"
create container Engine:
    action run needs x: Text:
        return base_op of x
    end
end

// app.wfl
include from "mid.wfl"
create new Engine as e:
end
display e.run("hi")

error[ERROR]: Semantic error in included file 'mid.wfl': Variable 'base_op' is not defined (fatal). #547 was the stdlib-function instance of "included files analyzed in isolation"; this is the same root for a user action in a sibling include.

Impact / workaround (context: the Scribe templating engine, written in WFL)

To ship on the current build, Scribe had to (1) live in a single file — cross-file of-references are fatal — and (2) expose its API only as container methods (engine.render_string(src, ctx)), because method dispatch is the one call form the analyzer never statically rejects. The natural multi-file, top-level-helper surface (render of tmpl and ctx across an include) is not consumable in a normal wfl program.wfl run. Completing #548 for the of form (and #547 for cross-include user actions) would remove both concessions.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions