Skip to content

Commit

Permalink
Merge branch 'nightly-with-mathlib' into new-variable
Browse files Browse the repository at this point in the history
  • Loading branch information
Kha authored Jul 23, 2024
2 parents c4ad42c + 5938dbb commit 2e2e0a0
Show file tree
Hide file tree
Showing 31 changed files with 1,054 additions and 741 deletions.
94 changes: 66 additions & 28 deletions src/Lean/Elab/App.lean
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Util.FindMVar
import Lean.Util.CollectFVars
import Lean.Parser.Term
import Lean.Meta.KAbstract
import Lean.Meta.Tactic.ElimInfo
Expand Down Expand Up @@ -711,6 +712,12 @@ structure Context where
```
theorem Eq.subst' {α} {motive : α → Prop} {a b : α} (h : a = b) : motive a → motive b
```
For another example, the term `isEmptyElim (α := α)` is an underapplied eliminator, and it needs
argument `α` to be elaborated eagerly to create a type-correct motive.
```
def isEmptyElim [IsEmpty α] {p : α → Sort _} (a : α) : p a := ...
example {α : Type _} [IsEmpty α] : id (α → False) := isEmptyElim (α := α)
```
-/
extraArgsPos : Array Nat

Expand All @@ -724,8 +731,8 @@ structure State where
namedArgs : List NamedArg
/-- User-provided arguments that still have to be processed. -/
args : List Arg
/-- Discriminants processed so far. -/
discrs : Array Expr := #[]
/-- Discriminants (targets) processed so far. -/
discrs : Array (Option Expr)
/-- Instance implicit arguments collected so far. -/
instMVars : Array MVarId := #[]
/-- Position of the next argument to be processed. We use it to decide whether the argument is the motive or a discriminant. -/
Expand All @@ -742,10 +749,7 @@ def mkMotive (discrs : Array Expr) (expectedType : Expr): MetaM Expr := do
let motiveBody ← kabstract motive discr
/- We use `transform (usedLetOnly := true)` to eliminate unnecessary let-expressions. -/
let discrType ← transform (usedLetOnly := true) (← instantiateMVars (← inferType discr))
let motive := Lean.mkLambda (← mkFreshBinderName) BinderInfo.default discrType motiveBody
unless (← isTypeCorrect motive) do
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motive}"
return motive
return Lean.mkLambda (← mkFreshBinderName) BinderInfo.default discrType motiveBody

/-- If the eliminator is over-applied, we "revert" the extra arguments. -/
def revertArgs (args : List Arg) (f : Expr) (expectedType : Expr) : TermElabM (Expr × Expr) :=
Expand All @@ -761,37 +765,58 @@ def revertArgs (args : List Arg) (f : Expr) (expectedType : Expr) : TermElabM (E
return (mkApp f val, mkForall (← mkFreshBinderName) BinderInfo.default valType expectedTypeBody)

/--
Construct the resulting application after all discriminants have bee elaborated, and we have
Construct the resulting application after all discriminants have been elaborated, and we have
consumed as many given arguments as possible.
-/
def finalize : M Expr := do
unless (← get).namedArgs.isEmpty do
throwError "failed to elaborate eliminator, unused named arguments: {(← get).namedArgs.map (·.name)}"
let some motive := (← get).motive?
| throwError "failed to elaborate eliminator, insufficient number of arguments"
trace[Elab.app.elab_as_elim] "motive: {motive}"
forallTelescope (← get).fType fun xs _ => do
trace[Elab.app.elab_as_elim] "xs: {xs}"
let mut expectedType := (← read).expectedType
trace[Elab.app.elab_as_elim] "expectedType:{indentD expectedType}"
let throwInsufficient := do
throwError "failed to elaborate eliminator, insufficient number of arguments, expected type:{indentExpr expectedType}"
let mut f := (← get).f
if xs.size > 0 then
-- under-application, specialize the expected type using `xs`
assert! (← get).args.isEmpty
try
expectedType ← instantiateForall expectedType xs
catch _ =>
throwError "failed to elaborate eliminator, insufficient number of arguments, expected type:{indentExpr expectedType}"
for x in xs do
let .forallE _ t b _ ← whnf expectedType | throwInsufficient
unless ← fullApproxDefEq <| isDefEq t (← inferType x) do
-- We can't assume that these binding domains were supposed to line up, so report insufficient arguments
throwInsufficient
expectedType := b.instantiate1 x
trace[Elab.app.elab_as_elim] "xs after specialization of expected type: {xs}"
else
-- over-application, simulate `revert`
-- over-application, simulate `revert` while generalizing the values of these arguments in the expected type
(f, expectedType) ← revertArgs (← get).args f expectedType
unless ← isTypeCorrect expectedType do
throwError "failed to elaborate eliminator, after generalizing over-applied arguments, expected type is type incorrect:{indentExpr expectedType}"
trace[Elab.app.elab_as_elim] "expectedType after processing:{indentD expectedType}"
let result := mkAppN f xs
trace[Elab.app.elab_as_elim] "result:{indentD result}"
let mut discrs := (← get).discrs
let idx := (← get).idx
if (← get).discrs.size < (← read).elimInfo.targetsPos.size then
if discrs.any Option.isNone then
for i in [idx:idx + xs.size], x in xs do
if (← read).elimInfo.targetsPos.contains i then
discrs := discrs.push x
let motiveVal ← mkMotive discrs expectedType
if let some tidx := (← read).elimInfo.targetsPos.indexOf? i then
discrs := discrs.set! tidx x
if let some idx := discrs.findIdx? Option.isNone then
-- This should not happen.
trace[Elab.app.elab_as_elim] "Internal error, missing target with index {idx}"
throwError "failed to elaborate eliminator, insufficient number of arguments"
trace[Elab.app.elab_as_elim] "discrs: {discrs.map Option.get!}"
let motiveVal ← mkMotive (discrs.map Option.get!) expectedType
unless (← isTypeCorrect motiveVal) do
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motiveVal}"
unless (← isDefEq motive motiveVal) do
throwError "failed to elaborate eliminator, invalid motive{indentExpr motiveVal}"
synthesizeAppInstMVars (← get).instMVars result
trace[Elab.app.elab_as_elim] "completed motive:{indentD motive}"
let result ← mkLambdaFVars xs (← instantiateMVars result)
return result

Expand Down Expand Up @@ -819,9 +844,9 @@ def getNextArg? (binderName : Name) (binderInfo : BinderInfo) : M (LOption Arg)
def setMotive (motive : Expr) : M Unit :=
modify fun s => { s with motive? := motive }

/-- Push the given expression into the `discrs` field in the state. -/
def addDiscr (discr : Expr) : M Unit :=
modify fun s => { s with discrs := s.discrs.push discr }
/-- Push the given expression into the `discrs` field in the state, where `i` is which target it is for. -/
def addDiscr (i : Nat) (discr : Expr) : M Unit :=
modify fun s => { s with discrs := s.discrs.set! i discr }

/-- Elaborate the given argument with the given expected type. -/
private def elabArg (arg : Arg) (argExpectedType : Expr) : M Expr := do
Expand Down Expand Up @@ -856,11 +881,11 @@ partial def main : M Expr := do
let motive ← mkImplicitArg binderType binderInfo
setMotive motive
addArgAndContinue motive
else if (← read).elimInfo.targetsPos.contains idx then
else if let some tidx := (← read).elimInfo.targetsPos.indexOf? idx then
match (← getNextArg? binderName binderInfo) with
| .some arg => let discr ← elabArg arg binderType; addDiscr discr; addArgAndContinue discr
| .some arg => let discr ← elabArg arg binderType; addDiscr tidx discr; addArgAndContinue discr
| .undef => finalize
| .none => let discr ← mkImplicitArg binderType binderInfo; addDiscr discr; addArgAndContinue discr
| .none => let discr ← mkImplicitArg binderType binderInfo; addDiscr tidx discr; addArgAndContinue discr
else match (← getNextArg? binderName binderInfo) with
| .some (.stx stx) =>
if (← read).extraArgsPos.contains idx then
Expand Down Expand Up @@ -922,10 +947,12 @@ def elabAppArgs (f : Expr) (namedArgs : Array NamedArg) (args : Array Arg)
let expectedType ← instantiateMVars expectedType
if expectedType.getAppFn.isMVar then throwError "failed to elaborate eliminator, expected type is not available"
let extraArgsPos ← getElabAsElimExtraArgsPos elimInfo
trace[Elab.app.elab_as_elim] "extraArgsPos: {extraArgsPos}"
ElabElim.main.run { elimInfo, expectedType, extraArgsPos } |>.run' {
f, fType
args := args.toList
namedArgs := namedArgs.toList
discrs := mkArray elimInfo.targetsPos.size none
}
else
ElabAppArgs.main.run { explicit, ellipsis, resultIsOutParamSupport } |>.run' {
Expand Down Expand Up @@ -955,19 +982,29 @@ where

/--
Collect extra argument positions that must be elaborated eagerly when using `elab_as_elim`.
The idea is that the contribute to motive inference. See comment at `ElamElim.Context.extraArgsPos`.
The idea is that they contribute to motive inference. See comment at `ElamElim.Context.extraArgsPos`.
-/
getElabAsElimExtraArgsPos (elimInfo : ElimInfo) : MetaM (Array Nat) := do
forallTelescope elimInfo.elimType fun xs type => do
let resultArgs := type.getAppArgs
let targets := type.getAppArgs
/- Compute transitive closure of fvars appearing in the motive and the targets. -/
let initMotiveFVars : CollectFVars.State := targets.foldl (init := {}) collectFVars
let motiveFVars ← xs.size.foldRevM (init := initMotiveFVars) fun i s => do
let x := xs[i]!
if elimInfo.motivePos == i || elimInfo.targetsPos.contains i || s.fvarSet.contains x.fvarId! then
return collectFVars s (← inferType x)
else
return s
/- Collect the extra argument positions -/
let mut extraArgsPos := #[]
for i in [:xs.size] do
let x := xs[i]!
unless elimInfo.targetsPos.contains i do
let xType ← inferType x
unless elimInfo.motivePos == i || elimInfo.targetsPos.contains i do
let xType ← x.fvarId!.getType
/- We only consider "first-order" types because we can reliably "extract" information from them. -/
if isFirstOrder xType
&& Option.isSome (xType.find? fun e => e.isFVar && resultArgs.contains e) then
if motiveFVars.fvarSet.contains x.fvarId!
|| (isFirstOrder xType
&& Option.isSome (xType.find? fun e => e.isFVar && motiveFVars.fvarSet.contains e.fvarId!)) then
extraArgsPos := extraArgsPos.push i
return extraArgsPos

Expand Down Expand Up @@ -1528,5 +1565,6 @@ builtin_initialize
registerTraceClass `Elab.app.args (inherited := true)
registerTraceClass `Elab.app.propagateExpectedType (inherited := true)
registerTraceClass `Elab.app.finalize (inherited := true)
registerTraceClass `Elab.app.elab_as_elim (inherited := true)

end Lean.Elab.Term
57 changes: 54 additions & 3 deletions src/Lean/Elab/Command.lean
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,64 @@ import Lean.Language.Basic

namespace Lean.Elab.Command

/--
A `Scope` records the part of the `CommandElabM` state that respects scoping,
such as the data for `universe`, `open`, and `variable` declarations, the current namespace,
and currently enabled options.
The `CommandElabM` state contains a stack of scopes, and only the top `Scope`
on the stack is read from or modified. There is always at least one `Scope` on the stack,
even outside any `section` or `namespace`, and each new pushed `Scope`
starts as a modified copy of the previous top scope.
-/
structure Scope where
/--
The component of the `namespace` or `section` that this scope is associated to.
For example, `section a.b.c` and `namespace a.b.c` each create three scopes with headers
named `a`, `b`, and `c`.
This is used for checking the `end` command. The "base scope" has `""` as its header.
-/
header : String
/--
The current state of all set options at this point in the scope. Note that this is the
full current set of options and does *not* simply contain the options set
while this scope has been active.
-/
opts : Options := {}
/-- The current namespace. The top-level namespace is represented by `Name.anonymous`. -/
currNamespace : Name := Name.anonymous
/-- All currently `open`ed namespaces and names. -/
openDecls : List OpenDecl := []
/-- The current list of names for universe level variables to use for new declarations. This is managed by the `universe` command. -/
levelNames : List Name := []
/-- section variables -/
/--
The current list of binders to use for new declarations.
This is managed by the `variable` command.
Each binder is represented in `Syntax` form, and it is re-elaborated
within each command that uses this information.
This is also used by commands, such as `#check`, to create an initial local context,
even if they do not work with binders per se.
-/
varDecls : Array (TSyntax ``Parser.Term.bracketedBinder) := #[]
/-- `include`d section variable names -/
includedVars : List Name := []
/-- Globally unique internal identifiers for the `varDecls` -/
/--
Globally unique internal identifiers for the `varDecls`.
There is one identifier per variable introduced by the binders
(recall that a binder such as `(a b c : Ty)` can produce more than one variable),
and each identifier is the user-provided variable name with a macro scope.
This is used by `TermElabM` in `Lean.Elab.Term.Context` to help with processing macros
that capture these variables.
-/
varUIds : Array Name := #[]
/-- noncomputable sections automatically add the `noncomputable` modifier to any declaration we cannot generate code for. -/
/--
If true (default: false), all declarations that fail to compile
automatically receive the `noncomputable` modifier.
A scope with this flag set is created by `noncomputable section`.
Recall that a new scope inherits all values from its parent scope,
so all sections and namespaces nested within a `noncomputable` section also have this flag set.
-/
isNoncomputable : Bool := false
deriving Inhabited

Expand Down Expand Up @@ -232,6 +277,7 @@ private def ioErrorToMessage (ctx : Context) (ref : Syntax) (err : IO.Error) : M
instance : MonadLiftT IO CommandElabM where
monadLift := liftIO

/-- Return the current scope. -/
def getScope : CommandElabM Scope := do pure (← get).scopes.head!

instance : MonadResolveName CommandElabM where
Expand Down Expand Up @@ -614,6 +660,11 @@ Interrupt and abort exceptions are caught but not logged.
private def liftAttrM {α} (x : AttrM α) : CommandElabM α := do
liftCoreM x

/--
Return the stack of all currently active scopes:
the base scope always comes last; new scopes are prepended in the front.
In particular, the current scope is always the first element.
-/
def getScopes : CommandElabM (List Scope) := do
pure (← get).scopes

Expand Down
2 changes: 1 addition & 1 deletion src/Lean/Elab/PreDefinition/Main.lean
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def checkTerminationByHints (preDefs : Array PreDefinition) : CoreM Unit := do
if !structural && !preDefsWithout.isEmpty then
let m := MessageData.andList (preDefsWithout.toList.map (m!"{·.declName}"))
let doOrDoes := if preDefsWithout.size = 1 then "does" else "do"
logErrorAt termBy.ref (m!"Incomplete set of `termination_by` annotations:\n"++
logErrorAt termBy.ref (m!"incomplete set of `termination_by` annotations:\n"++
m!"This function is mutually with {m}, which {doOrDoes} not have " ++
m!"a `termination_by` clause.\n" ++
m!"The present clause is ignored.")
Expand Down
19 changes: 9 additions & 10 deletions src/Lean/Elab/PreDefinition/Structural/BRecOn.lean
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Util.HasConstCache
import Lean.Meta.PProdN
import Lean.Meta.Match.MatcherApp.Transform
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.FunPacker
import Lean.Elab.PreDefinition.Structural.RecArgInfo

namespace Lean.Elab.Structural
Expand All @@ -21,11 +21,11 @@ private def throwToBelowFailed : MetaM α :=
partial def searchPProd (e : Expr) (F : Expr) (k : Expr → Expr → MetaM α) : MetaM α := do
match (← whnf e) with
| .app (.app (.const `PProd _) d1) d2 =>
(do searchPProd d1 (← mkAppM ``PProd.fst #[F]) k)
<|> (do searchPProd d2 (← mkAppM `PProd.snd #[F]) k)
(do searchPProd d1 (.proj ``PProd 0 F) k)
<|> (do searchPProd d2 (.proj ``PProd 1 F) k)
| .app (.app (.const `And _) d1) d2 =>
(do searchPProd d1 (← mkAppM `And.left #[F]) k)
<|> (do searchPProd d2 (← mkAppM `And.right #[F]) k)
(do searchPProd d1 (.proj `And 0 F) k)
<|> (do searchPProd d2 (.proj `And 1 F) k)
| .const `PUnit _
| .const `True _ => throwToBelowFailed
| _ => k e F
Expand Down Expand Up @@ -85,7 +85,7 @@ private def withBelowDict [Inhabited α] (below : Expr) (numIndParams : Nat)
return ((← mkFreshUserName `C), fun _ => pure t)
withLocalDeclsD CDecls fun Cs => do
-- We have to pack these canary motives like we packed the real motives
let packedCs ← positions.mapMwith packMotives motiveTypes Cs
let packedCs ← positions.mapMwith PProdN.packLambdas motiveTypes Cs
let belowDict := mkAppN pre packedCs
let belowDict := mkAppN belowDict finalArgs
trace[Elab.definition.structural] "initial belowDict for {Cs}:{indentExpr belowDict}"
Expand Down Expand Up @@ -250,7 +250,7 @@ def mkBRecOnConst (recArgInfos : Array RecArgInfo) (positions : Positions)
let brecOnAux := brecOnCons 0
-- Infer the type of the packed motive arguments
let packedMotiveTypes ← inferArgumentTypesN indGroup.numMotives brecOnAux
let packedMotives ← positions.mapMwith packMotives packedMotiveTypes motives
let packedMotives ← positions.mapMwith PProdN.packLambdas packedMotiveTypes motives

return fun n => mkAppN (brecOnCons n) packedMotives

Expand Down Expand Up @@ -289,12 +289,11 @@ def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Nat → Exp
let brecOn := brecOnConst recArgInfo.indIdx
let brecOn := mkAppN brecOn indexMajorArgs
let packedFTypes ← inferArgumentTypesN positions.size brecOn
let packedFArgs ← positions.mapMwith packFArgs packedFTypes FArgs
let packedFArgs ← positions.mapMwith PProdN.mkLambdas packedFTypes FArgs
let brecOn := mkAppN brecOn packedFArgs
let some poss := positions.find? (·.contains fnIdx)
| throwError "mkBrecOnApp: Could not find {fnIdx} in {positions}"
let brecOn ← if poss.size = 1 then pure brecOn else
mkPProdProjN (poss.getIdx? fnIdx).get! brecOn
let brecOn ← PProdN.proj poss.size (poss.getIdx? fnIdx).get! brecOn
mkLambdaFVars ys (mkAppN brecOn otherArgs)

end Lean.Elab.Structural
Loading

0 comments on commit 2e2e0a0

Please sign in to comment.