Fix MRE — Fix an LFortran Bug from a Minimal Reproducible Example
Given an MRE (produced by the create-mre skill or written by hand), reproduce
the LFortran bug, fix it in the compiler source, add an integration test, and
verify the full suites still pass.
Prerequisites
- The LFortran repository is the current working directory.
- A configured build directory.
AGENTS.mddocuments the standard dev config:cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DWITH_LLVM=ON \ -DWITH_STACKTRACE=yes./build1.shdoes this for you. build/src/binfirst onPATH, solfortranresolves to the in-tree build you are about to modify. Verify withwhich lfortranbefore starting — testing against a stale systemlfortranwastes an entire debug cycle.- A reference Fortran compiler on
PATH(gfortranpreferred; it is what thegfortranintegration-test label uses).
Inputs to Gather
Ask the user for (if not already provided):
- The MRE — the
.f90file(s) andrun.shthat reproduce the bug. By thecreate-mreconvention these are in the repository root.
Procedure
Phase 0: Read Project Guidelines
Read AGENTS.md in the LFortran repository root to understand project
conventions, coding style, testing practices, and contribution guidelines.
Note especially the architecture rules — they determine where a fix belongs:
- Type coercion and casting belong in AST→ASR (semantics). The LLVM/codegen backend must never infer or fix types; it only lowers what ASR gives it. If codegen appears to need a type workaround, the bug is upstream — fix it in semantics instead.
libasris frontend-independent. Never reference_lfortranor other frontend-specific names there.- No new C/C++ macros; use
constexpr, templates, or inline functions.
Follow all instructions therein, but the instructions in this SKILL.md file take precedence where they conflict.
Phase 1: Reproduce the Bug
- Read
run.shto understand the bug. - Run it to confirm the failure:
bash run.sh - Note the exact error message, error type (compilation error, runtime crash, wrong output), and the Fortran construct involved.
Do not proceed until you have reproduced the failure locally. A fix for a bug you have not observed is a guess.
Phase 2: Diagnose the Root Cause
- Analyze the error message to determine which compiler phase is failing:
- Parser (
src/lfortran/parser/): syntax errors, tokenizer failures - Semantics (
src/lfortran/semantics/): type errors, symbol resolution - ASR passes (
src/libasr/pass/): transformation errors - Code generation (
src/libasr/codegen/): LLVM IR generation failures
- Parser (
- Search the LFortran source for the error message text or error label to find
the code that produces it:
grep -rn "error text" src/ - Inspect the intermediate representations to see where the tree first goes
wrong — this is usually faster than reading code:
lfortran --show-ast <mre_file>.f90 lfortran --show-asr <mre_file>.f90 - Understand the code path that leads to the failure. Read surrounding code to understand the intended behavior.
- Identify the minimal fix needed. Fix the root cause, not the symptom.
Phase 3: Implement the Fix
- Make the code change in the LFortran source. Keep changes minimal and focused on the bug. Match the formatting of the file you are editing; do not reformat surrounding code.
- Rebuild:
cmake --build build -j - Re-run the MRE to verify the fix:
bash run.sh - The MRE should now succeed (compile and/or run correctly) with
lfortran.
If the fix doesn't work, iterate: re-diagnose, adjust, rebuild, and re-test.
Phase 4: Add an Integration Test
The integration test — not the MRE — is the deliverable of this skill. The MRE files stay untracked; the test is what gets committed.
- Look at existing integration tests in
integration_tests/to find similar tests (same Fortran construct, similar naming pattern). - Create a new
.f90file inintegration_tests/following the naming convention (e.g.intrinsic_name_NN.f90,derived_type_feature_NN.f90). Pick the next available number. - The test should be based on the MRE but written as a proper integration test:
- Include runtime checks using
if (result /= expected) error stopidioms. - Keep it minimal but cover the bug scenario.
- Include runtime checks using
- Register the test in
integration_tests/CMakeLists.txt:- Find the appropriate section (search for
macro(RUNand existingRUN(NAME ...)entries). - Add a
RUN(NAME <test_name> LABELS gfortran llvm)style entry. - Use at least the labels
gfortranandllvm. An unregistered test is dead code.
- Find the appropriate section (search for
- Verify the test compiles with the reference compiler — this confirms the
test is valid Fortran and does not depend on LFortran-specific behavior:
gfortran -o /tmp/test_ref integration_tests/<test_name>.f90 && /tmp/test_ref rm -f /tmp/test_ref - Verify the test compiles and runs with
lfortran:cd integration_tests ./run_tests.py -t <test_name>
Confirm the test actually captures the bug. AGENTS.md requires that every
fix PR demonstrate the test fails on main and passes on the branch. Verify
this explicitly — git stash your source change, rebuild, confirm the new test
fails, then restore and rebuild. If the test passes without your fix, it does
not cover the bug and the fix is not understood well enough.
Phase 5: Run Integration Tests
Run the full integration test suite:
cd integration_tests
./run_tests.py -j16 &> log
tail -n30 log
Always redirect test output to a log file and then examine it. Do not pipe
to tail directly — if you need more output than tail shows you have to
rerun the whole suite, which takes several minutes.
- If all tests pass, proceed to Phase 6.
- If any test fails:
- Examine the log to identify which test failed and why.
- Determine if the failure is caused by your change (a regression) or a pre-existing issue.
- If your change caused it, fix the regression, rebuild, and re-run tests.
- Repeat until all integration tests pass.
Phase 6: Run Reference Tests
Return to the LFortran root and run reference tests:
cd <lfortran-root>
./run_tests.py &> log
- If reference tests pass, proceed to Phase 7.
- If reference tests fail (expected when your fix changes compiler output):
- Update reference results:
./run_tests.py -u - Review the changes with
git diffto ensure all reference updates are correct and expected — they should all be consequences of your bug fix, not regressions. - If any reference change looks wrong, investigate and fix before proceeding.
- Update reference results:
Never run -u blindly. An unreviewed reference update can silently bake a
regression into the expected output.
Phase 7: Report
Summarize the work and let the user decide whether to commit. Do not commit or
push without being asked — AGENTS.md requires PRs to go to a fork, never
upstream, and the user may want to review the diff first.
Before staging anything, confirm the MRE scratch files (mre_*.f90,
re_*.f90, run.sh, run_re.sh) are not included — they are ignored by
.gitignore and are not part of the fix.
Print a summary:
Bug fixed!
Fix: <one-line description of what was changed>
Files modified:
<list of changed source files>
Integration test: integration_tests/<test_name>.f90
fails on main, passes on this branch — verified
Integration tests: pass
Reference tests: pass (<N> reference outputs updated, reviewed)
Not committed. Review the diff, then commit when ready.
If the user asks you to commit, follow the AGENTS.md commit conventions:
small single-topic commits, imperative mood, one bug = one MRE = one PR, and
never mix refactoring or formatting with a bug fix.
Tips
- Multiple errors: The MRE may expose more than one bug. First fix the bug that the MRE demonstrates. If you discover additional issues that block compiling and running the newly added test, fix them as well — but keep unrelated fixes to separate PRs.
- ASR passes: Many bugs live in ASR passes (
src/libasr/pass/). These transform the ASR tree and are a common source of codegen failures. - Semantic errors: If the bug is "not yet implemented", the fix likely involves adding a new case in the semantics or codegen visitor.
- Error message style: Per
AGENTS.md, messages are lowercase, show explicit kinds (e.g.integer(4)vsinteger(8)), and never expose internal ASR node names to users. - Modfile issues: If you see "Incompatible format: LFortran Modfile...",
the module files are stale — rebuild from scratch (
ninja -C build clean && cmake --build build -j) and ensurebuild/src/binis first onPATH. - Rebuild quickly:
cmake --build build -jonly recompiles changed files. Use it frequently during iteration. - Test naming: Look at nearby tests in
CMakeLists.txtfor naming conventions. Usually it's<feature>_<number>.