Repository navigation
Debug directives - #545
Merged
Merged
Debug directives#545
Conversation
# Conflicts: # hkmc2/shared/src/main/scala/hkmc2/CompilerCtx.scala # hkmc2/shared/src/main/scala/hkmc2/MLsCompiler.scala # hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala # hkmc2/shared/src/main/scala/hkmc2/semantics/Term.scala
Adapt debug directives to explicit annotation locations, erasure, and the new resolver. Preserve local-value debug scoping and refresh golden outputs. Validation: ctest, focused debug diff tests, and hkmc2AllTests/test (1,081 tests passed).
Share directive discovery, parsing, definition traversal, and trace scoping between the compiler and diff tests. Report parsing diagnostics once, honor method lowering traces, and inherit nested debug settings. Avoid reindenting the elaborator body and remove unused parsing overrides. Add diff tests for scoped and file output, plus explicit FIXME coverage for unsupported local parsing/optimization and class/module trace hooks. Include the latest golden output. Validation: ctest and hkmc2AllTests/test.
Warn when local debug annotations request parsing or optimization traces. Reuse definition scopes for class and module elaboration and lowering. Append file output through the filesystem abstraction and share one handler across imported units. Keep concurrent debugging writes best effort. Add diff regressions, a linear I/O check, and in-memory append coverage. Validate with ctest and hkmc2AllTests/test; include updated golden output.
LPTK
marked this pull request as ready for review
October 16, 2026 06:14
Reproduce, with their current wrong output: - diff-test commands (`:dl`, `:de`, `:dr`) muted inside definitions whose `@dbg` annotation does not enable the same phase; - lowering traces of a class with a superclass leaking into the statements that follow the class; - `:dp` parse traces redirected to a `#dbg(out: File(...))` destination; - absolute `File(...)` destinations crashing the compiler; - value definitions with parameter lists crashing lowering. Co-Authored-By: Claude Opus 5.5 <[email protected]>
A definition-local `@dbg` override replaced the trace logger's and the elaboration state's enablement wholesale, so an annotation that did not enable a phase (e.g. `@dbg(showUids: false)` under `:dl`) silently muted the block-wide tracing requested by `:dl`, `:de`, `:dr`, `:d`, or UCS scopes inside that definition. Separate the debugging that diff-test commands force (`doForceTrace`, `doForceDbg`) from the configurable part: overrides can still enable a phase, and disable a phase enabled by `#dbg`, but forced debugging stays on and keeps writing to standard output. Co-Authored-By: Claude Opus 5.5 <[email protected]>
For a class with an `extends` clause, the `@dbg` lowering scope wrapped the continuation-passing evaluation of the superclass, whose continuation also lowers every statement following the class, so those siblings were traced with the class's settings. Share the continuation-aware scoping that local values already used as `DebugOutputHandler.traceDefinitionThen`, and use it for both: the superclass and the class definition stay in the class's debug scope, while the remaining statements are lowered in the enclosing one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
`DebugOutputHandler.parse` sent every traced parse to the destination selected by `#dbg(out: ...)`, even when only the `:dp` diff-test command requested the trace, so a file destination swallowed `:dp` output. Like the other forced traces, use the selected destination only when `#dbg` itself enables parsing, and standard output otherwise. Co-Authored-By: Claude Opus 5.5 <[email protected]>
`DebugOutputHandler.emit` always built `baseDir / io.RelPath(path)`, and
`io.RelPath` rejects absolute paths with an exception, so a destination
such as `#dbg(out: File("/tmp/trace.log"))` crashed the compiler on its
first trace line. Resolve destinations like import paths instead: keep
absolute paths as they are, and resolve others against the base
directory, through a shared `io.Path.resolve`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The elaborator accepted `val f(x) = ...`, producing a value definition with parameters, which lowering cannot represent: top-level values hit an assertion in lowering, and local values used to lose their parameters silently and fail later in code generation. The debug-scoping refactor of value lowering extended the assertion to local values as well. Report a compilation error instead, and elaborate the value without its parameter lists, so uses of the parameters in the body are reported too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add a "Compiler Debugging" section describing the `#dbg` directive and the `@dbg` annotation: the available aspects, their syntax, output destinations, scoping rules, and current limitations. Remove the working notes in DBG_DIRECTIVE_PLAN.md, which this section supersedes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Document that explicit phase configuration selects the output destination even when a diff-test command also forces tracing. The implementation already behaves this way. Reviewed the six follow-up fixes through d0425a5. Validation: ctest, focused debug and invalid-definition regressions, and hkmc2AllTests/test.
# Conflicts: # hkmc2/shared/src/main/scala/hkmc2/codegen/Lowering.scala
Keep parser tracing in the diff-test :dp command and remove source-directed discovery and replay. Share IR display selection and formatting between the compiler and diff tests. Consolidate nine debug diff tests into four, retain scope and routing regressions, and write generated fixtures and logs under target without rmSync cleanup. Update the language reference and include regenerated goldens. Validation: ctest, dtest Debug, and hkmc2AllTests/test all pass.
# Conflicts: # hkmc2/shared/src/main/scala/hkmc2/semantics/Elaborator.scala
LPTK
commented
Oct 17, 2026
- Add the `showElaboratedTerm` debugging aspect, which prints the elaborated term in readable form with the settings of the `:sr` diff-test command, as `showIR` does for the IR. It shares the whole block/annotated definition selection with `showElaboratedTree`. - Remove the `resolution` aspect: it only traced the legacy resolver. New resolution happens during elaboration, so `elaboration` traces it. The `:dr` diff-test command still traces the legacy resolver. - Remove DebugOutputHandlerTest, which did not pay for its maintenance. - Restore the original indentation of local value lowering, a commented-out reference to `Assign`, and the original comment on the diff-test elaboration logger's `trace` override. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Record fields and named arguments such as `#dbg(out: ...)` may need names that are keywords. Instead of a dedicated parse rule for `out:`, parse any alphanumeric keyword followed by a colon as an identifier, in statement, rule, and simple-expression positions. No keyword-introduced syntax continues with a colon, and expressions are ascribed with `as`, so this is unambiguous; `true: e` is a key too. Selections such as `r.class` already worked, since the lexer makes them selection tokens. Co-Authored-By: Claude Opus 5.5 <[email protected]>
`@dbg` on a `let` binding was silently accepted and had no effect. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Relative `File(...)` destinations were resolved against the compiler's root base directory, so the same directive named different files depending on how a unit was compiled; in diff tests, the test file and the units it imports used different bases. Resolve them like imports, against the directory of the file containing the directive, when the directive is parsed: `DebugOutput.File` now holds the resolved path, and `DebugOutputHandler` no longer needs a base directory. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Render trace messages with an explicit printer policy, including symbols nested in products and collections. Keep numeric UIDs local to the printer compilation unit, and use disambiguated names for imported symbols. Cover UID scoping and error recovery with diff tests and update the golden outputs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GPT 6 Astra summary below.
This PR also does a bunch of refactoring to clean up the debugging logic of the codebase.
Compiler traces and tree/IR displays can now be requested in source code through
#dbg(...)directives and@dbg(...)definition annotations, in both the compiler and diff tests. Language reference, section 20 documents the syntax and behavior.elaboration,lowering, andoptimizations. New name resolution happens during elaboration, soelaborationtraces it; the legacy resolver is not traced by directives (:drstill traces it in diff tests).showParsedTree,showElaboratedTree,showElaboratedTerm(readable form, as in:sr),showLoweredTree,showIR, andshowOptimizedIR;showUidscontrols symbol UID display in traces.#dbg('lowering)or named booleans such as#dbg(lowering: false).out: StdIOorout: File("path"), with relative paths resolved against the declaring file's directory, as for imports. Each file is replaced on first use in a compilation session, then appended to, including across imported units.Parser tracing remains available through the diff-test command
:dp. Source directives are read after parsing, without a discovery/reparse pass or buffered diagnostic replay. Parsed-tree display remains available throughshowParsedTree.@dbgscopes tracing to functions, values, methods, classes, and modules. Nested annotations inherit enclosing trace settings, and continuations processing sibling statements restore the enclosing scope. Class lowering includes its superclass evaluation and companion module. Definition-localoptimizationssettings warn because optimization passes process whole programs.Diff-test commands force tracing independently of annotations. Forced-only traces go to the test output; when source settings also enable the phase, their destination applies. The compiler and diff tests share output routing, definition scoping, and tree/IR display helpers. File output uses
FileSystem.appendrather than repeatedly rewriting accumulated logs.The PR also diagnoses parameter lists on value definitions instead of letting them fail during lowering. It also parses any alphanumeric keyword followed by a colon as a key (as in
out: e,class: e, ortrue: e), so that record fields and named arguments can use keyword names.Validation:
ctest,dtest Debug, andhkmc2AllTests/testall pass. Four.mlsdebugging test files cover directives, diagnostics, displays, scoping, forced tracing, and file routing across imports. Generated fixtures and logs stay undertarget/, with normSynccleanup. A Scala check covers immutable in-memory filesystem snapshots after appends, which golden output cannot establish. Updated goldens are committed.Known limitations:
StdIOtraces go to process stdout rather than the diff-test output.