feat: meta phase restrictions

This commit is contained in:
Sebastian Ullrich 2025-06-07 14:46:43 +02:00
parent d10a85539a
commit 64e105c121
31 changed files with 246 additions and 114 deletions

View file

@ -7,6 +7,7 @@ module
prelude
import Init.Prelude
meta import Init.Prelude
set_option linter.missingDocs true -- keep it documented
/-!

View file

@ -9,7 +9,7 @@ module
prelude
import Init.Tactics
import Init.Meta
meta import Init.Meta
namespace Lean.Parser.Tactic.Conv

View file

@ -8,7 +8,7 @@ notation, basic datatypes and type classes
module
prelude
import Init.Prelude
meta import Init.Prelude
import Init.SizeOf
set_option linter.missingDocs true -- keep it documented

View file

@ -174,7 +174,7 @@ recommended_spelling "zero" for "0#n" in [BitVec.ofNat, «term__#__»]
recommended_spelling "one" for "1#n" in [BitVec.ofNat, «term__#__»]
/-- Unexpander for bitvector literals. -/
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNat] meta def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
| `($(_) $n $i:num) => `($i:num#$n)
| _ => throw ()
@ -183,7 +183,7 @@ scoped syntax:max term:max noWs "#'" noWs term:max : term
macro_rules | `($i#'$p) => `(BitVec.ofNatLT $i $p)
/-- Unexpander for bitvector literals without truncation. -/
@[app_unexpander BitVec.ofNatLT] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
@[app_unexpander BitVec.ofNatLT] meta def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
| `($(_) $i $p) => `($i#'$p)
| _ => throw ()

View file

@ -9,6 +9,7 @@ prelude
import Init.SimpLemmas
import Init.Data.Nat.Basic
import Init.Data.List.Notation
import Init.Data.Nat.Div.Basic
@[expose] section

View file

@ -6,7 +6,7 @@ Author: Leonardo de Moura
module
prelude
import Init.Data.Nat.Div.Basic
meta import Init.Data.Nat.Div.Basic
/-!
# Notation for `List` literals.

View file

@ -9,6 +9,7 @@ prelude
import Init.WF
import Init.WFTactics
import Init.Data.Nat.Basic
meta import Init.MetaTypes
@[expose] section

View file

@ -6,8 +6,7 @@ Author: Leonardo de Moura
module
prelude
import Init.Meta
import Init.Data.ToString.Basic
meta import Init.Meta
syntax:max "s!" interpolatedStr(term) : term

View file

@ -7,6 +7,7 @@ Authors: Shreyas Srinivas, François G. Dorais, Kim Morrison
module
prelude
meta import Init.Coe
import Init.Data.Array.Lemmas
import Init.Data.Array.MapIdx
import Init.Data.Array.InsertIdx

View file

@ -20,13 +20,13 @@ set_option linter.unusedVariables false in
def node_def (_ : Nat) {α : Sort u} {a : α} : NodeDef := .unit
@[app_unexpander node_def]
def nodeDefUnexpander : PrettyPrinter.Unexpander := fun stx => do
meta def nodeDefUnexpander : PrettyPrinter.Unexpander := fun stx => do
match stx with
| `($_ $id:num) => return mkIdent <| Name.mkSimple $ "#" ++ toString id.getNat
| _ => throw ()
@[app_unexpander NodeDef]
def NodeDefUnexpander : PrettyPrinter.Unexpander := fun _ => do
meta def NodeDefUnexpander : PrettyPrinter.Unexpander := fun _ => do
return mkIdent <| Name.mkSimple "NodeDef"
end Lean.Grind

View file

@ -57,25 +57,25 @@ theorem nestedProof_congr (p q : Prop) (h : p = q) (hp : p) (hq : q) : HEq (@nes
subst h; apply HEq.refl
@[app_unexpander nestedProof]
def nestedProofUnexpander : PrettyPrinter.Unexpander := fun stx => do
meta def nestedProofUnexpander : PrettyPrinter.Unexpander := fun stx => do
match stx with
| `($_ $p:term) => `($p)
| _ => throw ()
@[app_unexpander MatchCond]
def matchCondUnexpander : PrettyPrinter.Unexpander := fun stx => do
meta def matchCondUnexpander : PrettyPrinter.Unexpander := fun stx => do
match stx with
| `($_ $p:term) => `($p)
| _ => throw ()
@[app_unexpander EqMatch]
def eqMatchUnexpander : PrettyPrinter.Unexpander := fun stx => do
meta def eqMatchUnexpander : PrettyPrinter.Unexpander := fun stx => do
match stx with
| `($_ $lhs:term $rhs:term) => `($lhs = $rhs)
| _ => throw ()
@[app_unexpander offset]
def offsetUnexpander : PrettyPrinter.Unexpander := fun stx => do
meta def offsetUnexpander : PrettyPrinter.Unexpander := fun stx => do
match stx with
| `($_ $lhs:term $rhs:term) => `($lhs + $rhs)
| _ => throw ()
@ -88,7 +88,7 @@ many `NatCast.natCast` applications which is too verbose. We add the following
unexpander to cope with this issue.
-/
@[app_unexpander NatCast.natCast]
def natCastUnexpander : PrettyPrinter.Unexpander := fun stx => do
meta def natCastUnexpander : PrettyPrinter.Unexpander := fun stx => do
match stx with
| `($_ $a:term) => `(↑$a)
| _ => throw ()

View file

@ -13,6 +13,8 @@ import Init.MetaTypes
import Init.Syntax
import Init.Data.Array.GetLit
import Init.Data.Option.BasicAux
meta import Init.Data.Array.Basic
meta import Init.Syntax
namespace Lean
@ -1610,7 +1612,7 @@ macro (name := declareSimpLikeTactic) doc?:(docComment)?
else
pure (← `(``dsimp), ← `("dsimp"), ← `($[$doc?:docComment]? syntax (name := $tacName) $tacToken:str optConfig (discharger)? (&" only")? (" [" (simpErase <|> simpLemma),* "]")? (location)? : tactic))
`($stx:command
@[macro $tacName] def expandSimp : Macro := fun s => do
@[macro $tacName] meta def expandSimp : Macro := fun s => do
let cfg ← `(optConfig| $cfg)
let s := s.setKind $kind
let s := s.setArg 0 (mkAtomFrom s[0] $tkn (canonical := true))

View file

@ -8,7 +8,6 @@ Notation for operators defined at Prelude.lean
module
prelude
import Init.Prelude
import Init.Coe
set_option linter.missingDocs true -- keep it documented
@ -27,14 +26,14 @@ of a lean file. For example, `def foo := 1` is a `command`, as is
`namespace Foo` and `end Foo`. Commands generally have an effect on the state of
adding something to the environment (like a new definition), as well as
commands like `variable` which modify future commands within a scope. -/
def command : Category := {}
meta def command : Category := {}
/-- `term` is the builtin syntax category for terms. A term denotes an expression
in lean's type theory, for example `2 + 2` is a term. The difference between
`Term` and `Expr` is that the former is a kind of syntax, while the latter is
the result of elaboration. For example `by simp` is also a `Term`, but it elaborates
to different `Expr`s depending on the context. -/
def term : Category := {}
meta def term : Category := {}
/-- `tactic` is the builtin syntax category for tactics. These appear after
`by` in proofs, and they are programs that take in the proof context
@ -44,28 +43,28 @@ a term of the expected type. For example, `simp` is a tactic, used in:
example : 2 + 2 = 4 := by simp
```
-/
def tactic : Category := {}
meta def tactic : Category := {}
/-- `doElem` is a builtin syntax category for elements that can appear in the `do` notation.
For example, `let x ← e` is a `doElem`, and a `do` block consists of a list of `doElem`s. -/
def doElem : Category := {}
meta def doElem : Category := {}
/-- `structInstFieldDecl` is the syntax category for value declarations for fields in structure instance notation.
For example, the `:= 1` and `| 0 => 0 | n + 1 => n` in `{ x := 1, f | 0 => 0 | n + 1 => n }` are in the `structInstFieldDecl` class. -/
def structInstFieldDecl : Category := {}
meta def structInstFieldDecl : Category := {}
/-- `level` is a builtin syntax category for universe levels.
This is the `u` in `Sort u`: it can contain `max` and `imax`, addition with
constants, and variables. -/
def level : Category := {}
meta def level : Category := {}
/-- `attr` is a builtin syntax category for attributes.
Declarations can be annotated with attributes using the `@[...]` notation. -/
def attr : Category := {}
meta def attr : Category := {}
/-- `stx` is a builtin syntax category for syntax. This is the abbreviated
parser notation used inside `syntax` and `macro` declarations. -/
def stx : Category := {}
meta def stx : Category := {}
/-- `prio` is a builtin syntax category for priorities.
Priorities are used in many different attributes.
@ -73,7 +72,7 @@ Higher numbers denote higher priority, and for example typeclass search will
try high priority instances before low priority.
In addition to literals like `37`, you can also use `low`, `mid`, `high`, as well as
add and subtract priorities. -/
def prio : Category := {}
meta def prio : Category := {}
/-- `prec` is a builtin syntax category for precedences. A precedence is a value
that expresses how tightly a piece of syntax binds: for example `1 + 2 * 3` is
@ -84,7 +83,7 @@ In addition to literals like `37`, there are some special named precedence level
* `max` for the highest precedence used in term parsers (not actually the maximum possible value)
* `lead` for the precedence of terms not supposed to be used as arguments
and you can also add and subtract precedences. -/
def prec : Category := {}
meta def prec : Category := {}
end Parser.Category

View file

@ -9,10 +9,11 @@ module
prelude
import Init.Data.ToString.Basic
import Init.Data.Array.Subarray
import Init.Conv
import Init.Meta
import Init.While
meta import Init.Data.Option.Basic
meta import Init.Data.Array.Subarray
namespace Lean
@ -142,13 +143,13 @@ macro tk:"calc" steps:calcSteps : conv =>
`(conv| tactic => calc%$tk $steps)
end Lean
@[app_unexpander Unit.unit] def unexpandUnit : Lean.PrettyPrinter.Unexpander
@[app_unexpander Unit.unit] meta def unexpandUnit : Lean.PrettyPrinter.Unexpander
| `($(_)) => `(())
@[app_unexpander List.nil] def unexpandListNil : Lean.PrettyPrinter.Unexpander
@[app_unexpander List.nil] meta def unexpandListNil : Lean.PrettyPrinter.Unexpander
| `($(_)) => `([])
@[app_unexpander List.cons] def unexpandListCons : Lean.PrettyPrinter.Unexpander
@[app_unexpander List.cons] meta def unexpandListCons : Lean.PrettyPrinter.Unexpander
| `($(_) $x $tail) =>
match tail with
| `([]) => `([$x])
@ -157,105 +158,105 @@ end Lean
| _ => throw ()
| _ => throw ()
@[app_unexpander List.toArray] def unexpandListToArray : Lean.PrettyPrinter.Unexpander
@[app_unexpander List.toArray] meta def unexpandListToArray : Lean.PrettyPrinter.Unexpander
| `($(_) [$xs,*]) => `(#[$xs,*])
| _ => throw ()
@[app_unexpander Prod.mk] def unexpandProdMk : Lean.PrettyPrinter.Unexpander
@[app_unexpander Prod.mk] meta def unexpandProdMk : Lean.PrettyPrinter.Unexpander
| `($(_) $x ($y, $ys,*)) => `(($x, $y, $ys,*))
| `($(_) $x $y) => `(($x, $y))
| _ => throw ()
@[app_unexpander ite] def unexpandIte : Lean.PrettyPrinter.Unexpander
@[app_unexpander ite] meta def unexpandIte : Lean.PrettyPrinter.Unexpander
| `($(_) $c $t $e) => `(if $c then $t else $e)
| _ => throw ()
@[app_unexpander Eq.ndrec] def unexpandEqNDRec : Lean.PrettyPrinter.Unexpander
@[app_unexpander Eq.ndrec] meta def unexpandEqNDRec : Lean.PrettyPrinter.Unexpander
| `($(_) $m $h) => `($h ▸ $m)
| _ => throw ()
@[app_unexpander Eq.rec] def unexpandEqRec : Lean.PrettyPrinter.Unexpander
@[app_unexpander Eq.rec] meta def unexpandEqRec : Lean.PrettyPrinter.Unexpander
| `($(_) $m $h) => `($h ▸ $m)
| _ => throw ()
@[app_unexpander Exists] def unexpandExists : Lean.PrettyPrinter.Unexpander
@[app_unexpander Exists] meta def unexpandExists : Lean.PrettyPrinter.Unexpander
| `($(_) fun $x:ident => ∃ $xs:binderIdent*, $b) => `(∃ $x:ident $xs:binderIdent*, $b)
| `($(_) fun $x:ident => $b) => `(∃ $x:ident, $b)
| `($(_) fun ($x:ident : $t) => $b) => `(∃ ($x:ident : $t), $b)
| _ => throw ()
@[app_unexpander Sigma] def unexpandSigma : Lean.PrettyPrinter.Unexpander
@[app_unexpander Sigma] meta def unexpandSigma : Lean.PrettyPrinter.Unexpander
| `($(_) fun ($x:ident : $t) => $b) => `(($x:ident : $t) × $b)
| _ => throw ()
@[app_unexpander PSigma] def unexpandPSigma : Lean.PrettyPrinter.Unexpander
@[app_unexpander PSigma] meta def unexpandPSigma : Lean.PrettyPrinter.Unexpander
| `($(_) fun ($x:ident : $t) => $b) => `(($x:ident : $t) ×' $b)
| _ => throw ()
@[app_unexpander Subtype] def unexpandSubtype : Lean.PrettyPrinter.Unexpander
@[app_unexpander Subtype] meta def unexpandSubtype : Lean.PrettyPrinter.Unexpander
| `($(_) fun ($x:ident : $type) => $p) => `({ $x : $type // $p })
| `($(_) fun $x:ident => $p) => `({ $x // $p })
| _ => throw ()
@[app_unexpander TSyntax] def unexpandTSyntax : Lean.PrettyPrinter.Unexpander
@[app_unexpander TSyntax] meta def unexpandTSyntax : Lean.PrettyPrinter.Unexpander
| `($f [$k]) => `($f $k)
| _ => throw ()
@[app_unexpander TSyntaxArray] def unexpandTSyntaxArray : Lean.PrettyPrinter.Unexpander
@[app_unexpander TSyntaxArray] meta def unexpandTSyntaxArray : Lean.PrettyPrinter.Unexpander
| `($f [$k]) => `($f $k)
| _ => throw ()
@[app_unexpander Syntax.TSepArray] def unexpandTSepArray : Lean.PrettyPrinter.Unexpander
@[app_unexpander Syntax.TSepArray] meta def unexpandTSepArray : Lean.PrettyPrinter.Unexpander
| `($f [$k] $sep) => `($f $k $sep)
| _ => throw ()
@[app_unexpander GetElem.getElem] def unexpandGetElem : Lean.PrettyPrinter.Unexpander
@[app_unexpander GetElem.getElem] meta def unexpandGetElem : Lean.PrettyPrinter.Unexpander
| `($_ $array $index $_) => `($array[$index])
| _ => throw ()
@[app_unexpander getElem!] def unexpandGetElem! : Lean.PrettyPrinter.Unexpander
@[app_unexpander getElem!] meta def unexpandGetElem! : Lean.PrettyPrinter.Unexpander
| `($_ $array $index) => `($array[$index]!)
| _ => throw ()
@[app_unexpander getElem?] def unexpandGetElem? : Lean.PrettyPrinter.Unexpander
@[app_unexpander getElem?] meta def unexpandGetElem? : Lean.PrettyPrinter.Unexpander
| `($_ $array $index) => `($array[$index]?)
| _ => throw ()
@[app_unexpander Array.empty] def unexpandArrayEmpty : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.empty] meta def unexpandArrayEmpty : Lean.PrettyPrinter.Unexpander
| _ => `(#[])
@[app_unexpander Array.mkArray0] def unexpandMkArray0 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray0] meta def unexpandMkArray0 : Lean.PrettyPrinter.Unexpander
| _ => `(#[])
@[app_unexpander Array.mkArray1] def unexpandMkArray1 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray1] meta def unexpandMkArray1 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1) => `(#[$a1])
| _ => throw ()
@[app_unexpander Array.mkArray2] def unexpandMkArray2 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray2] meta def unexpandMkArray2 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1 $a2) => `(#[$a1, $a2])
| _ => throw ()
@[app_unexpander Array.mkArray3] def unexpandMkArray3 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray3] meta def unexpandMkArray3 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1 $a2 $a3) => `(#[$a1, $a2, $a3])
| _ => throw ()
@[app_unexpander Array.mkArray4] def unexpandMkArray4 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray4] meta def unexpandMkArray4 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1 $a2 $a3 $a4) => `(#[$a1, $a2, $a3, $a4])
| _ => throw ()
@[app_unexpander Array.mkArray5] def unexpandMkArray5 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray5] meta def unexpandMkArray5 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1 $a2 $a3 $a4 $a5) => `(#[$a1, $a2, $a3, $a4, $a5])
| _ => throw ()
@[app_unexpander Array.mkArray6] def unexpandMkArray6 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray6] meta def unexpandMkArray6 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1 $a2 $a3 $a4 $a5 $a6) => `(#[$a1, $a2, $a3, $a4, $a5, $a6])
| _ => throw ()
@[app_unexpander Array.mkArray7] def unexpandMkArray7 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray7] meta def unexpandMkArray7 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1 $a2 $a3 $a4 $a5 $a6 $a7) => `(#[$a1, $a2, $a3, $a4, $a5, $a6, $a7])
| _ => throw ()
@[app_unexpander Array.mkArray8] def unexpandMkArray8 : Lean.PrettyPrinter.Unexpander
@[app_unexpander Array.mkArray8] meta def unexpandMkArray8 : Lean.PrettyPrinter.Unexpander
| `($(_) $a1 $a2 $a3 $a4 $a5 $a6 $a7 $a8) => `(#[$a1, $a2, $a3, $a4, $a5, $a6, $a7, $a8])
| _ => throw ()
@ -360,13 +361,13 @@ namespace Lean
/-- Unexpander for the `{ x }` notation. -/
@[app_unexpander singleton]
def singletonUnexpander : Lean.PrettyPrinter.Unexpander
meta def singletonUnexpander : Lean.PrettyPrinter.Unexpander
| `($_ $a) => `({ $a:term })
| _ => throw ()
/-- Unexpander for the `{ x, y, ... }` notation. -/
@[app_unexpander insert]
def insertUnexpander : Lean.PrettyPrinter.Unexpander
meta def insertUnexpander : Lean.PrettyPrinter.Unexpander
| `($_ $a { $ts:term,* }) => `({$a:term, $ts,*})
| _ => throw ()

View file

@ -695,7 +695,7 @@ syntax (name := dsimp) "dsimp" optConfig (discharger)? (&" only")?
A `simpArg` is either a `*`, `-lemma` or a simp lemma specification
(which includes the `↑` `↓` `←` specifications for pre, post, reverse rewriting).
-/
def simpArg := simpStar.binary `orelse (simpErase.binary `orelse simpLemma)
meta def simpArg := simpStar.binary `orelse (simpErase.binary `orelse simpLemma)
/-- A simp args list is a list of `simpArg`. This is the main argument to `simp`. -/
syntax simpArgs := " [" simpArg,* "]"
@ -704,7 +704,7 @@ syntax simpArgs := " [" simpArg,* "]"
A `dsimpArg` is similar to `simpArg`, but it does not have the `simpStar` form
because it does not make sense to use hypotheses in `dsimp`.
-/
def dsimpArg := simpErase.binary `orelse simpLemma
meta def dsimpArg := simpErase.binary `orelse simpLemma
/-- A dsimp args list is a list of `dsimpArg`. This is the main argument to `dsimp`. -/
syntax dsimpArgs := " [" dsimpArg,* "]"

View file

@ -14,6 +14,7 @@ import Lean.Compiler.NeverExtractAttr
import Lean.Compiler.IR
import Lean.Compiler.CSimpAttr
import Lean.Compiler.FFI
import Lean.Compiler.MetaAttr
import Lean.Compiler.NoncomputableAttr
import Lean.Compiler.Main
import Lean.Compiler.AtMostOnce -- TODO: delete after we port code generator to Lean

View file

@ -0,0 +1,34 @@
/-
Copyright (c) 2025 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich
-/
prelude
import Lean.EnvExtension
namespace Lean
builtin_initialize metaExt : TagDeclarationExtension ← mkTagDeclarationExtension
/-- Marks in the environment extension that the given declaration has been declared by the user as `meta`. -/
def addMeta (env : Environment) (declName : Name) : Environment :=
metaExt.tag env declName
/-- Returns true iff the user has declared the given declaration as `meta`. -/
def isMeta (env : Environment) (declName : Name) : Bool :=
metaExt.isTagged env declName
/--
Returns the IR phases of the given declaration that should be considered accessible. Does not take
additional IR loaded for language server purposes into account.
-/
@[export lean_get_ir_phases]
def getIRPhases (env : Environment) (declName : Name) : IRPhases := Id.run do
if !env.header.isModule then
return .all
match env.getModuleIdxFor? declName with
| some idx => if isMeta env declName then .comptime else env.header.modules[idx.toNat]?.map (·.irPhases) |>.get!
-- Allow `meta`->non-`meta` acesses in the same module
| none => if isMeta env declName then .comptime else .all
end Lean

View file

@ -32,5 +32,5 @@ def elabAuxDef : CommandElab
let id := id.replacePrefix ns Name.anonymous -- TODO: replace with def _root_.id
elabCommand <|
← `($[$doc?:docComment]? $[$attrs?:attributes]?
def $(mkIdentFrom (mkNullNode suggestion) id (canonical := true)):ident : $ty := $body)
meta def $(mkIdentFrom (mkNullNode suggestion) id (canonical := true)):ident : $ty := $body)
| _ => throwUnsupportedSyntax

View file

@ -10,6 +10,7 @@ import Lean.Elab.BindersUtil
import Lean.Elab.SyntheticMVars
import Lean.Elab.PreDefinition.TerminationHint
import Lean.Elab.Match
import Lean.Compiler.MetaAttr
namespace Lean.Elab.Term
open Meta
@ -109,6 +110,7 @@ def declareTacticSyntax (tactic : Syntax) (name? : Option Name := none) : TermEl
let decl := Declaration.defnDecl { name, levelParams := [], type, value, hints := .opaque,
safety := DefinitionSafety.safe }
addDecl decl
modifyEnv (addMeta · name)
compileDecl decl
return name

View file

@ -206,7 +206,7 @@ unsafe def elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax) (expectedType? :
to '{.ofConstName ``IO}' or '{.ofConstName ``CommandElabM}'."
addAndCompileExprForEval declName r (allowSorry := bang)
-- `evalConst` may emit IO, but this is collected by `withIsolatedStreams` below.
let r ← toMessageData <$> evalConst t declName
let r ← toMessageData <$> evalConst t declName (checkMeta := !Elab.inServer.get (← getOptions))
return { eval := pure r, printVal := some (← inferType e) }
let (output, exOrRes) ← IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get (← getOptions)) do
try

View file

@ -56,13 +56,18 @@ inductive RecKind where
| «partial» | «nonrec» | default
deriving Inhabited
/-- Codegen-relevant modifiers. -/
inductive ComputeKind where
| regular | «meta» | «noncomputable»
deriving Inhabited
/-- Flags and data added to declarations (eg docstrings, attributes, `private`, `unsafe`, `partial`, ...). -/
structure Modifiers where
/-- Input syntax, used for adjusting declaration range (unless missing) -/
stx : TSyntax ``Parser.Command.declModifiers := ⟨.missing⟩
docString? : Option (TSyntax ``Parser.Command.docComment) := none
visibility : Visibility := Visibility.regular
isNoncomputable : Bool := false
computeKind : ComputeKind := .regular
recKind : RecKind := RecKind.default
isUnsafe : Bool := false
attrs : Array Attribute := #[]
@ -77,14 +82,29 @@ def Modifiers.isProtected : Modifiers → Bool
| _ => false
def Modifiers.isPartial : Modifiers → Bool
| { recKind := .partial, .. } => true
| _ => false
| { recKind := .partial, .. } => true
| _ => false
/--
Whether the declaration is explicitly `partial` or should be considered as such via `meta`. In the
latter case, elaborators should not produce an error if partialty is unnecessary.
-/
def Modifiers.isInferredPartial : Modifiers → Bool
| { recKind := .partial, .. } => true
| { computeKind := .meta, .. } => true
| _ => false
def Modifiers.isNonrec : Modifiers → Bool
| { recKind := .nonrec, .. } => true
| _ => false
/-- Adds attribute `attr` in `modifiers`, at the end -/
def Modifiers.isMeta (m : Modifiers) : Bool :=
m.computeKind matches .meta
def Modifiers.isNoncomputable (m : Modifiers) : Bool :=
m.computeKind matches .noncomputable
/-- Adds attribute `attr` in `modifiers` -/
def Modifiers.addAttr (modifiers : Modifiers) (attr : Attribute) : Modifiers :=
{ modifiers with attrs := modifiers.attrs.push attr }
@ -105,7 +125,7 @@ instance : ToFormat Modifiers := ⟨fun m =>
| .regular => []
| .protected => [f!"protected"]
| .private => [f!"private"])
++ (if m.isNoncomputable then [f!"noncomputable"] else [])
++ (match m.computeKind with | .regular => [] | .meta => [f!"meta"] | .noncomputable => [f!"noncomputable"])
++ (match m.recKind with | RecKind.partial => [f!"partial"] | RecKind.nonrec => [f!"nonrec"] | _ => [])
++ (if m.isUnsafe then [f!"unsafe"] else [])
++ m.attrs.toList.map (fun attr => format attr)
@ -127,18 +147,18 @@ section Methods
variable [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadInfoTree m] [MonadLiftT IO m]
/-- Elaborate declaration modifiers (i.e., attributes, `partial`, `private`, `protected`, `unsafe`, `noncomputable`, doc string)-/
/-- Elaborate declaration modifiers (i.e., attributes, `partial`, `private`, `protected`, `unsafe`, `meta`, `noncomputable`, doc string)-/
def elabModifiers (stx : TSyntax ``Parser.Command.declModifiers) : m Modifiers := do
let docCommentStx := stx.raw[0]
let attrsStx := stx.raw[1]
let visibilityStx := stx.raw[2]
let isNoncomputable :=
let computeKind :=
if stx.raw[3].isNone then
false
.regular
else if stx.raw[3][0].getKind == ``Parser.Command.meta then
false -- TODO: handle `meta` declarations
.meta
else
true
.noncomputable
let unsafeStx := stx.raw[4]
let recKind :=
if stx.raw[5].isNone then
@ -159,9 +179,8 @@ def elabModifiers (stx : TSyntax ``Parser.Command.declModifiers) : m Modifiers :
| none => pure #[]
| some attrs => elabDeclAttrs attrs
return {
stx, docString?, visibility, recKind, attrs,
stx, docString?, visibility, computeKind, recKind, attrs,
isUnsafe := !unsafeStx.isNone
isNoncomputable
}
/--

View file

@ -993,9 +993,12 @@ def getKindForLetRecs (mainHeaders : Array DefViewElabHeader) : DefKind :=
else DefKind.«def»
def getModifiersForLetRecs (mainHeaders : Array DefViewElabHeader) : Modifiers := {
isNoncomputable := mainHeaders.any fun h => h.modifiers.isNoncomputable
recKind := if mainHeaders.any fun h => h.modifiers.isPartial then RecKind.partial else RecKind.default
isUnsafe := mainHeaders.any fun h => h.modifiers.isUnsafe
computeKind :=
if mainHeaders.any (·.modifiers.isNoncomputable) then .noncomputable
else if mainHeaders.any (·.modifiers.isMeta) then .meta
else .regular
recKind := if mainHeaders.any fun h => h.modifiers.isInferredPartial then RecKind.partial else RecKind.default
isUnsafe := mainHeaders.any fun h => h.modifiers.isUnsafe
}
/--

View file

@ -204,7 +204,7 @@ def main : Parser :=
keywordCore "module" (fun _ s => s) (fun _ s => { s with isModule := true }) >>
keywordCore "prelude" (fun _ s => s.pushImport `Init) (fun _ s => s) >>
many (keywordCore "private" (setIsExported true) (setIsExported false) >>
keywordCore "meta" (setIsMeta true) (setIsMeta false) >>
keywordCore "meta" (setIsMeta false) (setIsMeta true) >>
keyword "import" >>
keywordCore "all" (setImportAll false) (setImportAll true) >>
moduleIdent)

View file

@ -5,6 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Init.ShareCommon
import Lean.Compiler.MetaAttr
import Lean.Compiler.NoncomputableAttr
import Lean.Util.CollectLevelParams
import Lean.Util.NumObjs
@ -123,6 +124,20 @@ private def reportTheoremDiag (d : TheoremVal) : TermElabM Unit := do
-- let info
logInfo <| MessageData.trace { cls := `theorem } m!"{d.name}" (#[sizeMsg] ++ constOccsMsg)
-- TODO: should become part of the compiler to deal with erasure
private def checkMeta (preDef : PreDefinition) : TermElabM Unit := do
preDef.value.forEach' fun e => do
if e.isAutoParam then
return false
if let .const c .. := e then
match getIRPhases (← getEnv) c, preDef.modifiers.isMeta with
| .runtime, true =>
throwError "Invalid meta definition, '{.ofConstName c}' must be `meta` to access"
| .comptime, false =>
throwError "Invalid definition, may not access `meta` declaration '{.ofConstName c}'"
| _, _ => pure ()
return true
private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List Name) (applyAttrAfterCompilation := true) (cacheProofs := true) : TermElabM Unit :=
withRef preDef.ref do
let preDef ← abstractNestedProofs (cache := cacheProofs) preDef
@ -158,8 +173,11 @@ private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List N
withSaveInfoContext do -- save new env
addTermInfo' preDef.ref (← mkConstWithLevelParams preDef.declName) (isBinder := true)
applyAttributesOf #[preDef] AttributeApplicationTime.afterTypeChecking
if preDef.modifiers.isNoncomputable then
modifyEnv fun env => addNoncomputable env preDef.declName
match preDef.modifiers.computeKind with
| .meta => modifyEnv (addMeta · preDef.declName)
| .noncomputable => modifyEnv (addNoncomputable · preDef.declName)
| _ => pure ()
checkMeta preDef
if compile && shouldGenCodeFor preDef then
compileDecl decl
if applyAttrAfterCompilation then

View file

@ -326,13 +326,22 @@ def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLC
else if preDefs.any (·.modifiers.isUnsafe) then
addAndCompileUnsafe preDefs
preDefs.forM (·.termination.ensureNone "unsafe")
else if preDefs.any (·.modifiers.isPartial) then
for preDef in preDefs do
if preDef.modifiers.isPartial && !(← whnfD preDef.type).isForall then
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
addAndCompilePartial preDefs
preDefs.forM (·.termination.ensureNone "partial")
else
if preDefs.any (·.modifiers.isInferredPartial) then
let mut isPartial := true
for preDef in preDefs do
if !(← whnfD preDef.type).isForall then
if preDef.modifiers.isPartial then
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
else
-- `meta` should not imply `partial` in this case
isPartial := false
if isPartial then
addAndCompilePartial preDefs
preDefs.forM (·.termination.ensureNone "partial")
continue
ensureFunIndReservedNamesAvailable preDefs
try
checkCodomainsLevel preDefs

View file

@ -273,7 +273,7 @@ private def declareSyntaxCatQuotParser (catName : Name) : CommandElabM Unit := d
let quotSymbol := "`(" ++ suffix ++ "| "
let name := catName ++ `quot
let cmd ← `(
@[term_parser] def $(mkIdent name) : Lean.ParserDescr :=
@[term_parser] meta def $(mkIdent name) : Lean.ParserDescr :=
Lean.ParserDescr.node `Lean.Parser.Term.quot $(quote Lean.Parser.maxPrec)
(Lean.ParserDescr.node $(quote name) $(quote Lean.Parser.maxPrec)
(Lean.ParserDescr.binary `andthen (Lean.ParserDescr.symbol $(quote quotSymbol))
@ -295,7 +295,7 @@ private def declareSyntaxCatQuotParser (catName : Name) : CommandElabM Unit := d
let attrName := catName.appendAfter "_parser"
let catDeclName := ``Lean.Parser.Category ++ catName
setEnv (← Parser.registerParserCategory (← getEnv) attrName catName catBehavior catDeclName)
let cmd ← `($[$docString?]? def $(mkIdentFrom stx[2] (`_root_ ++ catDeclName) (canonical := true)) : Lean.Parser.Category := {})
let cmd ← `($[$docString?]? meta def $(mkIdentFrom stx[2] (`_root_ ++ catDeclName) (canonical := true)) : Lean.Parser.Category := {})
declareSyntaxCatQuotParser catName
elabCommand cmd
@ -396,10 +396,10 @@ def addMacroScopeIfLocal [MonadQuotation m] [Monad m] (name : Name) (attrKind :
let attrInstances := attrInstances.getD { elemsAndSeps := #[] }
let attrInstances := attrInstances.push attrInstance
let d ← if let some lhsPrec := lhsPrec? then
`($[$doc?:docComment]? @[$attrInstances,*] def $declName:ident : Lean.TrailingParserDescr :=
`($[$doc?:docComment]? @[$attrInstances,*] meta def $declName:ident : Lean.TrailingParserDescr :=
ParserDescr.trailingNode $(quote stxNodeKind) $(quote prec) $(quote lhsPrec) $val)
else
`($[$doc?:docComment]? @[$attrInstances,*] def $declName:ident : Lean.ParserDescr :=
`($[$doc?:docComment]? @[$attrInstances,*] meta def $declName:ident : Lean.ParserDescr :=
ParserDescr.node $(quote stxNodeKind) $(quote prec) $val)
trace `Elab fun _ => d
withMacroExpansion stx d <| elabCommand d
@ -409,7 +409,7 @@ def addMacroScopeIfLocal [MonadQuotation m] [Monad m] (name : Name) (attrKind :
-- TODO: nonatomic names
let (val, _) ← runTermElabM fun _ => Term.toParserDescr (mkNullNode ps) Name.anonymous
let stxNodeKind := (← getCurrNamespace) ++ declName.getId
let stx' ← `($[$doc?:docComment]? def $declName:ident : Lean.ParserDescr := ParserDescr.nodeWithAntiquot $(quote (toString declName.getId)) $(quote stxNodeKind) $val)
let stx' ← `($[$doc?:docComment]? meta def $declName:ident : Lean.ParserDescr := ParserDescr.nodeWithAntiquot $(quote (toString declName.getId)) $(quote stxNodeKind) $val)
withMacroExpansion stx stx' <| elabCommand stx'
def checkRuleKind (given expected : SyntaxNodeKind) : Bool :=

View file

@ -139,6 +139,21 @@ structure ModuleData where
entries : Array (Name × Array EnvExtensionEntry)
deriving Inhabited
/-- Phases for which some IR is available for execution. -/
inductive IRPhases where
/-- Available for execution in the final native code. -/
| runtime
/-- Available for execution during elaboration. -/
| comptime
/-- Available during run time and compile time. -/
| all
deriving Inhabited, BEq, Repr
/-- Import including information resulting from processing of the entire import DAG. -/
structure EffectiveImport extends Import where
/-- Phases for which the import's IR is available. -/
irPhases : IRPhases
/-- Environment fields that are not used often. -/
structure EnvironmentHeader where
/--
@ -157,14 +172,22 @@ structure EnvironmentHeader where
/-- Compacted regions for all imported modules. Objects in compacted memory regions do no require any memory management. -/
regions : Array CompactedRegion := #[]
/--
Name of all imported modules (directly and indirectly).
The index of a module name in the array equals the `ModuleIdx` for the same module.
Direct and transitive imports. Modules are given with their effective import modifiers, not their
original ones. Each module is listed at most once. The index of a module in the array equals the
`ModuleIdx` for the same module.
-/
moduleNames : Array Name := #[]
modules : Array EffectiveImport := #[]
/-- Module data for all imported modules. -/
moduleData : Array ModuleData := #[]
deriving Nonempty
/--
Name of all imported modules (directly and indirectly).
The index of a module name in the array equals the `ModuleIdx` for the same module.
-/
def EnvironmentHeader.moduleNames (header : EnvironmentHeader) : Array Name :=
header.modules.map (·.module)
namespace Kernel
structure Diagnostics where
@ -1159,7 +1182,7 @@ def isSafeDefinition (env : Environment) (declName : Name) : Bool :=
| _ => false
def getModuleIdx? (env : Environment) (moduleName : Name) : Option ModuleIdx :=
env.header.moduleNames.findIdx? (· == moduleName)
env.header.modules.findIdx? (·.module == moduleName)
end Environment
@ -1819,7 +1842,7 @@ where
else
return env
private structure ImportedModule extends Import where
private structure ImportedModule extends EffectiveImport where
/-- All loaded incremental compacted regions. -/
parts : Array (ModuleData × CompactedRegion)
@ -1883,7 +1906,7 @@ private def findOLeanParts (mod : Name) : IO (Array System.FilePath) := do
partial def importModulesCore
(imports : Array Import) (isModule := false) (arts : NameMap ModuleArtifacts := {}) :
ImportStateM Unit := do
go imports (importAll := true) (isExported := isModule)
go imports (importAll := true) (isExported := isModule) (isMeta := false)
if isModule then
for i in imports do
if let some mod := (← get).moduleNameMap[i.module]?.bind (·.mainModule?) then
@ -1928,7 +1951,7 @@ For implementation purposes, we represent elements in the lattice using two flag
`none` then is represented by not visiting a module at all.
-/
where go (imports : Array Import) (importAll : Bool) (isExported : Bool) := do
where go (imports : Array Import) (importAll isExported isMeta : Bool) := do
for i in imports do
-- `B = none`?
if !(i.isExported || importAll) then
@ -1937,13 +1960,17 @@ where go (imports : Array Import) (importAll : Bool) (isExported : Bool) := do
let importAll := !isModule || (importAll && i.importAll)
-- `B ≥ public`?
let isExported := isExported && i.isExported
let irPhases := if isMeta || i.isMeta then .comptime else .runtime
let goRec imports := do
go (importAll := importAll) (isExported := isExported) imports
go (importAll := importAll) (isExported := isExported) (isMeta := isMeta || i.isMeta) imports
if let some mod := (← get).moduleNameMap[i.module]? then
-- when module is already imported, bump flags
if importAll && !mod.importAll || isExported && !mod.isExported then
let importAll := importAll || mod.importAll
let isExported := isExported || mod.isExported
let irPhases := if irPhases == mod.irPhases then irPhases else .all
if importAll != mod.importAll || isExported != mod.isExported || irPhases != mod.irPhases then
modify fun s => { s with moduleNameMap := s.moduleNameMap.insert i.module { mod with
importAll, isExported }}
importAll, isExported, irPhases }}
-- bump entire closure
if let some mod := mod.mainModule? then
goRec mod.imports
@ -1961,7 +1988,7 @@ where go (imports : Array Import) (importAll : Bool) (isExported : Bool) := do
let some (baseMod, _) := parts[0]? | unreachable!
goRec baseMod.imports
modify fun s => { s with
moduleNameMap := s.moduleNameMap.insert i.module { i with importAll, isExported, parts }
moduleNameMap := s.moduleNameMap.insert i.module { i with importAll, isExported, irPhases, parts }
moduleNames := s.moduleNames.push i.module
}
@ -2053,10 +2080,9 @@ def finalizeImport (s : ImportState) (imports : Array Import) (opts : Options) (
extraConstNames := {}
extensions := exts
header := {
trustLevel, imports, moduleData
isModule
trustLevel, imports, moduleData, isModule
modules := modules.map (·.toEffectiveImport)
regions := modules.flatMap (·.parts.map (·.2))
moduleNames := s.moduleNames
}
}
let publicConstants : ConstMap := SMap.fromHashMap publicConstantMap false
@ -2198,12 +2224,27 @@ def displayStats (env : Environment) : IO Unit := do
unless fmt.isNil do IO.println (" " ++ toString (Format.nest 2 (extDescr.statsFn s.state)))
IO.println (" number of imported entries: " ++ toString (s.importedEntries.foldl (fun sum es => sum + es.size) 0))
/--
Evaluate the given declaration under the given environment to a value of the given type.
This function is only safe to use if the type matches the declaration's type in the environment
and if `enableInitializersExecution` has been used before importing any modules. -/
@[extern "lean_eval_const"]
unsafe opaque evalConst (α) (env : @& Environment) (opts : @& Options) (constName : @& Name) : Except String α
private unsafe opaque evalConstCore (α) (env : @& Environment) (opts : @& Options) (constName : @& Name) : Except String α
@[extern "lean_get_ir_phases"]
private opaque getIRPhases (env : Environment) (constName : Name) : IRPhases
/--
Evaluates the given declaration under the given environment to a value of the given type.
This function is only safe to use if the type matches the declaration's type in the environment
and if `enableInitializersExecution` has been used before importing any modules.
If `checkMeta` is true (the default), the function checks that the constant is declared or imported
as `meta` or otherwise fails with an error. It should only be set to `false` in cases where it is
acceptable for code to work only in the language server, where more IR is loaded, such as in
`#eval`.
-/
unsafe def evalConst (α) (env : @& Environment) (opts : @& Options) (constName : @& Name) (checkMeta := true) : Except String α :=
if checkMeta && getIRPhases env constName == .runtime then
throw ("cannot evaluate non-`meta` constant '" ++ toString constName ++ "'")
else
evalConstCore α env opts constName
private def throwUnexpectedType {α} (typeName : Name) (constName : Name) : ExceptT String Id α :=
throw ("unexpected type at '" ++ toString constName ++ "', `" ++ toString typeName ++ "` expected")

View file

@ -420,7 +420,7 @@ where
if let some (some processed) ← old.processedResult.get? then
-- ...and the edit is after the second-next command (see note [Incremental Parsing])...
if let some nextCom ← processed.firstCmdSnap.get? then
if let some nextNextCom ← processed.firstCmdSnap.get? then
if let some nextNextCom ← nextCom.nextCmdSnap?.bindM (·.get?) then
if (← isBeforeEditPos nextNextCom.parserState.pos) then
-- ...go immediately to next snapshot
return (← unchanged old old.stx oldSuccess.parserState)

View file

@ -147,8 +147,8 @@ See also `Lean.matchConstStructure` for a less restrictive version.
| _ => failK ()
| _ => failK ()
unsafe def evalConst [Monad m] [MonadEnv m] [MonadError m] [MonadOptions m] (α) (constName : Name) : m α := do
ofExcept <| (← getEnv).evalConst α (← getOptions) constName
unsafe def evalConst [Monad m] [MonadEnv m] [MonadError m] [MonadOptions m] (α) (constName : Name) (checkMeta := true) : m α := do
ofExcept <| (← getEnv).evalConst (checkMeta := checkMeta) α (← getOptions) constName
unsafe def evalConstCheck [Monad m] [MonadEnv m] [MonadError m] [MonadOptions m] (α) (typeName : Name) (constName : Name) : m α := do
ofExcept <| (← getEnv).evalConstCheck α (← getOptions) typeName constName

View file

@ -11,7 +11,7 @@ options get_default_options() {
opts = opts.update({"debug", "terminalTacticsAsSorry"}, false);
// switch to `true` for ABI-breaking changes affecting meta code;
// see also next option!
opts = opts.update({"interpreter", "prefer_native"}, false);
opts = opts.update({"interpreter", "prefer_native"}, true);
// switch to `false` when enabling `prefer_native` should also affect use
// of built-in parsers in quotations; this is usually the case, but setting
// both to `true` may be necessary for handling non-builtin parsers with

View file

@ -1,6 +1,6 @@
syntaxPrec.lean:1:17-1:21: error: unexpected token '<|>'; expected ':'
[Elab.command] @[term_parser 1000]
def «termFoo*_» : Lean.ParserDescr✝ :=
meta def «termFoo*_» : Lean.ParserDescr✝ :=
ParserDescr.node✝ `«termFoo*_» 1022
(ParserDescr.binary✝ `andthen (ParserDescr.symbol✝ "foo")
(ParserDescr.binary✝ `orelse (ParserDescr.nodeWithAntiquot✝ "*" `token.«*» (ParserDescr.symbol✝ "*"))