Skip to content

feat: add Environment.hasExposedBody helper#13868

Open
kim-em wants to merge 4 commits into
leanprover:masterfrom
kim-em:feat/Environment-hasExposedBody
Open

feat: add Environment.hasExposedBody helper#13868
kim-em wants to merge 4 commits into
leanprover:masterfrom
kim-em:feat/Environment-hasExposedBody

Conversation

@kim-em
Copy link
Copy Markdown
Collaborator

@kim-em kim-em commented May 28, 2026

This PR adds Lean.Environment.hasExposedBody — a small helper that asks "does env export a body for n to downstream modules?". The idiom

env.setExporting true |>.find? n |>.any (·.hasValue)

was duplicated inline in at least eight places in src/Lean/, under five different local names (exposed, exported, isExposed, bodyExposed, plus inline uses). It also appears in Mathlib (Mathlib/Tactic/Simps/Basic.lean, Mathlib/Tactic/Translate/Core.lean).

Pull it out, document the corner cases (axioms, declarations not in the environment, the non-module case), and update all eight inline call sites under src/Lean/:

  • Lean/Elab/Deriving/Basic.lean
  • Lean/Elab/PreDefinition/{WF,Structural,PartialFixpoint}/Eqns.lean
  • Lean/Meta/Constructions/SparseCasesOn.lean
  • Lean/Meta/Eqns.lean
  • Lean/Meta/MethodSpecs.lean

The last two wrap the check with !env.header.isModule || … to treat the non-module case uniformly as exposed; they keep that guard explicitly, calling env.hasExposedBody n for the inner check.

One related site in Lean/Elab/Print.lean uses .any (·.isDefinition) instead of .any (·.hasValue) (a strictly narrower check that only matches defs, not theorems/opaques), so it's left as-is rather than coerced into the new helper.

This is a pure refactor; no behaviour change is intended.

🤖 Prepared with Claude Code

kim-em added 2 commits May 28, 2026 10:50
The idiom

  env.setExporting true |>.find? n |>.any (·.hasValue)

answers a real, self-contained question — does `env` export a body for `n` to
downstream modules? — and was duplicated inline in at least eight places under
five different local names (`exposed`, `exported`, `isExposed`, `bodyExposed`,
plus inline uses). It also appears in Mathlib (`Mathlib/Tactic/Simps/Basic.lean`,
`Mathlib/Tactic/Translate/Core.lean`).

Pull it out as a small `Environment.hasExposedBody` helper with a docstring
clarifying the corner cases (axioms, declarations not in the environment, the
non-module case). Update all eight inline callers under `src/Lean/` to use it.

Two of the callers (`Lean/Meta/Eqns.lean`, `Lean/Meta/MethodSpecs.lean`) wrap
the check with `!env.header.isModule || …` so that the non-module case is
treated as exposed; they keep that guard explicitly, calling
`env.hasExposedBody n` for the inner check.

This is a pure refactor; no behaviour change is intended.
Per review:

- Rewrite the `hasExposedBody` docstring. The previous wording said `n` could
  be a "definition (or `instance`, `theorem`, etc.)" — but `ConstantInfo.hasValue`
  with the default `allowOpaque := false` returns `true` only for `defnInfo`, so
  the function returns `false` for theorems and opaques. State that plainly, and
  re-frame the non-module note: the suggested
  `!env.header.isModule || env.hasExposedBody n` pattern is a *policy* (bypass
  the body-exposed check outside modules) rather than a fact about `n`'s body.

- In `Lean/Meta/MethodSpecs.lean`, bind `env` once rather than calling
  `(← getEnv)` twice in the same line.

- Add a small test in `tests/elab/hasExposedBody.lean` covering exposed `def`,
  sealed `def`, theorem, opaque, axiom, inductive, and a missing name.
@kim-em kim-em changed the title feat(Environment): add Environment.hasExposedBody feat: add Environment.hasExposedBody helper May 28, 2026
The helper was placed right after setExporting (line ~660), but Environment.find?
isn't defined until line ~855 in the same file. Move the helper to right after
find? so it's visible at the use site.
@kim-em kim-em added the changelog-language Language features and metaprograms label May 28, 2026
@kim-em kim-em requested a review from Kha May 28, 2026 11:29
kim-em added a commit to kim-em/mathlib4 that referenced this pull request May 28, 2026
Vierkantor flagged that the `setExporting true |>.find? n |>.any (·.hasValue)`
idiom should really live in core. leanprover/lean4#13868 adds
`Environment.hasExposedBody` for exactly this. Leave the inline check in place
for now (so the PR remains buildable on the current toolchain), and add a
note next to it pointing at the follow-up refactor.

Also note the other in-tree caller of the same idiom in
`Mathlib/Tactic/Translate/Core.lean` even though it is strictly off-topic for
this PR — it'll save a future grep.
@github-actions github-actions Bot added the toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN label May 28, 2026
@mathlib-lean-pr-testing
Copy link
Copy Markdown

Mathlib CI status (docs):

  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase f6d12123d788929e4a5dfbde43535d122ab5dc4f --onto 2cd98639c40d2d2a026dd599093b70f55f55ffaf. You can force Mathlib CI using the force-mathlib-ci label. (2026-05-28 12:07:57)

@leanprover-bot
Copy link
Copy Markdown
Collaborator

Reference manual CI status:

  • ❗ Reference manual CI will not be attempted unless your PR branches off the nightly-with-manual branch. Try git rebase f6d12123d788929e4a5dfbde43535d122ab5dc4f --onto 803553a556fd82fa1060efb0c43eda542130cb16. You can force reference manual CI using the force-manual-ci label. (2026-05-28 12:07:58)

`guard` needs an `Alternative` instance, which `CommandElabM` lacks, so the
test failed to elaborate. Use `unless`/`if` + `throwError` instead, which need
no `Alternative`.
Copy link
Copy Markdown
Member

@Kha Kha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!

Comment thread src/Lean/Environment.lean
Comment on lines +844 to +856
/-- Returns `true` iff `n` resolves, in `env`'s exported view, to a `def` with a value.

Concretely this checks whether downstream modules can see a reducible body for `n`
(an exposed definition or an `abbrev`). Returns `false` for theorems and opaque
declarations (this uses `hasValue` with `allowOpaque := false`), axioms, inductives,
constructors, recursors, declarations not in the environment, and `def`s whose body
is sealed by the module system.

Outside the module system, `setExporting true` is a no-op, so this collapses to
"does `n` resolve to a `def` in the current environment?". Callers that instead
want to *bypass* the body-exposed check entirely outside modules (e.g. for name-
privacy decisions, where there is no sealing boundary anyway) should write that
policy explicitly: `!env.header.isModule || env.hasExposedBody n`. -/
Copy link
Copy Markdown
Member

@Kha Kha May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too much information. "Checks if, in the public scope (Environment.isExporting), the given name refers to a definition with a visible body, i.e. ConstantInfo.hasValue. Recall that outside the module system, this is any definition."

I'm pretty sure the !isModule cases are simply wrong

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

Labels

changelog-language Language features and metaprograms toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants