Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions Manual/ModuleSystem.lean
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,11 @@ When it comes to actual code execution, there is no point to a definition withou
Thus, in order to eagerly know what definitions _might_ be executed at compile time and so need to be available including their bodies (in some executable shape), any definition used as an entry point to compile-time execution has to be tagged with the new `meta` modifier.
This is automatically done in built-in metaprogramming syntax such as `syntax`, `macro`, and `elab` but may need to be done explicitly when manually applying metaprogramming attributes such as `@[app_delab]`.

A `meta` definition may access (and thus invoke) any `meta` or non-`meta` definition of the current module.
For accessing imported definitions, the definition must either have been marked as `meta` when it was declared or the import must be marked as such (`meta import` when the accessing definition is in the private scope and `public meta import` otherwise).

A `meta` definition may only access (and thus invoke) other `meta` definitions; a non-`meta` definition likewise may only access other non-`meta` definitions.
For imported definitions, the `meta` marker can be added after the fact using `meta import`.
`meta import`ing a definition already in the meta phase leaves it in that phase.
In addition, the import must be public if the imported definition may be compile-time executed outside the current module, i.e. if it is reachable from some public `meta` definition in the current module: use `public meta import` or, if already `meta`, `public import`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
In addition, the import must be public if the imported definition may be compile-time executed outside the current module, i.e. if it is reachable from some public `meta` definition in the current module: use `public meta import` or, if already `meta`, `public import`.
In addition, the import must be public if the imported constant may be executed at compile time outside the current module.
In other words, if the imported `meta` constant is reachable from some public `meta` definition in the current module, then use `public meta import`.
If the constant is already `meta`, then `public import` is sufficient.

Copy link
Member Author

Choose a reason for hiding this comment

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

I usually prefer "declaration" over "constant" in user-facing text, is there some canonized stance on that?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, but there should be. We typically use "declaration" in the manual to refer to the syntactic form of a declaration (that is, a certain subset of the commands). What about "name" here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't see a better-fitting word than "definition" here.

This is usually the case, unless a definition was imported solely for use in local metaprograms such as `local syntax/macro/elab/...`.
```
module

Expand All @@ -141,6 +143,15 @@ local elab "my_elab" : command => do
...
```

For convenience, `meta` also implies `partial`.
This can be overridden by giving an explicit `termination_by` metric (such as one suggested by `termination_by?`), which may be necessary when the type of the definition is not known to be `Nonempty`.

As a guideline, it is usually preferable to keep the amount of `meta` annotations as small as possible.
This avoids locking otherwise-reusable declarations into the meta phase and it helps the build system avoid more rebuilds.
Thus, when a metaprogram depends on other code that does not itself need to be marked `meta`, this other code should be located in a separate module and not marked `meta`.
Only the final module that actually registers a metaprogram needs the helpers as `meta` definitions.
It should use `public meta import` to import those helpers and then define its metaprograms using built-in syntax like `elab`, using `meta def`, or using `meta section`.

# Common Errors and Patterns

The following list contains common errors one might encounter when using the module system and especially porting existing files to the module system.
Expand All @@ -164,6 +175,11 @@ The following list contains common errors one might encounter when using the mod
You might also see this as a kernel error when a tactic directly emits proof terms referencing specific declarations without going through the elaborator, such as for proof by reflection.
In this case, there is no readily available trace for debugging; consider using `@[expose] section`s generously on the closure of relevant modules.

: "failed to compile 'partial' definition" on `meta` definition

This can happen when a definition with a type that is not known to be `Nonempty` is marked `meta` or moved into a `meta section`, which both imply `partial` without a termination metric.
Use `termination_by?` to make the previously implicitly inferred termination metric explicit, or provide a `Nonempty` instance.

## Recipe for Porting Existing Files

Start by enabling the module system throughout all files with minimal breaking changes:
Expand Down