feat: structure instance notation elaboration improvements (#7717)

This PR changes how `{...}`/`where` notation ("structure instance
notation") elaborates. The notation now tries to simulate a flat
representation as much as possible, without exposing the details of
subobjects. Features:
- When fields are elaborated, their expected types now have a couple
reductions applied. For all projections and constructors associated to
the structure and its parents, projections of constructors are reduced
and constructors of projections are eta reduced, and also implementation
detail local variables are zeta reduced in propositions (so tactic
proofs should never see them anymore). Furthermore, field values are
beta reduced automatically in successive field types. The example in
[mathlib4#12129](https://github.com/leanprover-community/mathlib4/issues/12129#issuecomment-2056134533)
now shows a goal of `0 = 0` rather than `{ toFun := fun x => x }.toFun 0
= 0`.
- All parents can now be used as field names, not just the subobject
parents. These are like additional sources but with three constraints:
every field of the value must be used, the fields must not overlap with
other provided fields, and every field of the specified parent must be
provided for. Similar to sources, the values are hoisted to `let`s if
they are not already variables, to avoid multiple evaluation. They are
implementation detail local variables, so they get unfolded for
successive fields.
- All class parents are now used to fill in missing fields, not just the
subobject parents. Closes #6046. Rules: (1) only those parents whose
fields are a subset of the remaining fields are considered, (2) parents
are considered only before any fields are elaborated, and (3) only those
parents whose type can be computed are considered (this can happen if a
parent depends on another parent, which is possible since #7302).
- Default values and autoparams now respect the resolution order
completely: each field has at most one default value definition that can
provide for it. The algorithm that tries to unstick default values by
walking up the subobject hierarchy has been removed. If there are
applications of default value priorities, we might consider it in a
future release.
- The resulting constructors are now fully packed. This is implemented
by doing structure eta reduction of the elaborated expressions.
- "Magic field definitions" (as reported [on
Zulip](https://leanprover.zulipchat.com/#narrow/channel/113489-new-members/topic/Where.20is.20sSup.20defined.20on.20submodules.3F/near/499578795))
have been eliminated. This was where fields were being solved for by
unification, tricking the default value system into thinking they had
actually been provided. Now the default value system keeps track of
which fields it has actually solved for, and which fields the user did
not provide. Explicit structure fields (the default kind) without any
explicit value definition will result in an error. If it was solved for
by unification, the error message will include the inferred value, like
"field 'f' must be explicitly provided, its synthesized value is v"
- When the notation is used in patterns, it now no longer inserts fields
using class parents, and it no longer applies autoparams or default
values. The motivation is that one expects patterns to match only the
given fields. This is still imperfect, since fields might be solved for
indirectly.
- Elaboration now attempts error recovery. Extraneous fields log errors
and are ignored, missing fields are filled with `sorry`.

This is a breaking change, but generally the mitigation is to remove
`dsimp only` from the beginnings of proofs. Sometimes "magic fields"
need to be provided — four possible mitigations are (1) to provide the
field, (2) to provide `_` for the value of the field, (3) to add `..` to
the structure instance notation, (4) or decide to modify the `structure`
command to make the field implicit. Lastly, sometimes parent instances
don't apply when they should. This could be because some of the provided
fields overlap with the class, or it could be that the parent depends on
some of the fields for synthesis — and as parents are only considered
before any fields are elaborated, such parents might not be possible to
use — we will look into refining this further.

There is also a change to elaboration: now the `afterTypeChecking`
attributes are run with all `structure` data set up (e.g. the list of
parents, along with all parent projections in the environment). This is
necessary since attributes like `@[ext]` use structure instance
notation, and the notation needs all this data to be set up now.
This commit is contained in:
Kyle Miller 2025-03-30 10:40:36 -07:00 committed by GitHub
parent 176e8bc077
commit 3f98f6bc07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 1595 additions and 1114 deletions

View file

@ -488,7 +488,7 @@ instance instCCPOPProd [CCPO α] [CCPO β] : CCPO (α ×' β) where
csup c := ⟨CCPO.csup (PProd.chain.fst c), CCPO.csup (PProd.chain.snd c)⟩
csup_spec := by
intro ⟨a, b⟩ c hchain
dsimp
try dsimp -- TODO(kmill) remove
constructor
next =>
intro ⟨h₁, h₂⟩ ⟨a', b'⟩ cab

View file

@ -121,6 +121,12 @@ structure ElabHeaderResult extends PreElabHeaderResult where
indFVar : Expr
deriving Inhabited
/-- An intermediate step for mutual inductive elaboration. See `InductiveElabDescr` -/
structure InductiveElabStep3 where
/-- Finalize the inductive type, after they are all added to the environment, after auxiliary definitions are added, and after computed fields are registered.
The `levelParams`, `params`, and `replaceIndFVars` arguments of `prefinalize` are still valid here. -/
finalize : TermElabM Unit := pure ()
/-- An intermediate step for mutual inductive elaboration. See `InductiveElabDescr`. -/
structure InductiveElabStep2 where
/-- The constructors produced by `InductiveElabStep1`. -/
@ -133,9 +139,7 @@ structure InductiveElabStep2 where
/-- Step to finalize term elaboration, done immediately after universe level processing is complete. -/
finalizeTermElab : TermElabM Unit := pure ()
/-- Like `finalize`, but occurs before `afterTypeChecking` attributes. -/
prefinalize (levelParams : List Name) (params : Array Expr) (replaceIndFVars : Expr → MetaM Expr) : TermElabM Unit := fun _ _ _ => pure ()
/-- Finalize the inductive type, after they are all added to the environment, after auxiliary definitions are added, and after computed fields are registered. -/
finalize (levelParams : List Name) (params : Array Expr) (replaceIndFVars : Expr → MetaM Expr) : TermElabM Unit := fun _ _ _ => pure ()
prefinalize (levelParams : List Name) (params : Array Expr) (replaceIndFVars : Expr → MetaM Expr) : TermElabM InductiveElabStep3 := fun _ _ _ => pure {}
deriving Inhabited
/-- An intermediate step for mutual inductive elaboration. See `InductiveElabDescr`. -/
@ -160,7 +164,7 @@ Elaboration occurs in the following steps:
- Elaboration of constructors is finalized, with additional tasks done by each `InductiveStep2.collectUniverses`.
- The inductive family is added to the environment and is checked by the kernel.
- Attributes and other finalization activities are performed, including those defined
by `InductiveStep2.prefinalize` and `InductiveStep2.finalize`.
by `InductiveStep2.prefinalize` and `InductiveStep3.finalize`.
-/
structure InductiveElabDescr where
mkInductiveView : Modifiers → Syntax → TermElabM InductiveElabStep1
@ -1048,9 +1052,9 @@ private def elabInductiveViewsPostprocessing (views : Array InductiveView) (res
let ref := view0.ref
applyComputedFields views -- NOTE: any generated code before this line is invalid
liftTermElabM <| withMCtx res.mctx <| withLCtx res.lctx res.localInsts do
for elab' in res.elabs do elab'.prefinalize res.levelParams res.params res.replaceIndFVars
let finalizers ← res.elabs.mapM fun elab' => elab'.prefinalize res.levelParams res.params res.replaceIndFVars
for view in views do withRef view.declId <| Term.applyAttributesAt view.declName view.modifiers.attrs .afterTypeChecking
for elab' in res.elabs do elab'.finalize res.levelParams res.params res.replaceIndFVars
for elab' in finalizers do elab'.finalize
applyDerivingHandlers views
runTermElabM fun _ => Term.withDeclName view0.declName do withRef ref do
for view in views do withRef view.declId <| Term.applyAttributesAt view.declName view.modifiers.attrs .afterCompilation

View file

@ -81,7 +81,7 @@ private partial def getFieldOrigin (structName field : Name) : MetaM StructureFi
return fi
open Meta in
private partial def printStructure (id : Name) (levelParams : List Name) (numParams : Nat) (type : Expr)
private partial def printStructure (id : Name) (levelParams : List Name) (numParams : Nat) (type : Expr) (ctor : Name)
(isUnsafe : Bool) : CommandElabM Unit := do
let env ← getEnv
let kind := if isClass env id then "class" else "structure"
@ -107,8 +107,9 @@ private partial def printStructure (id : Name) (levelParams : List Name) (numPar
let paramMap : NameMap Expr ← params.foldlM (init := {}) fun paramMap param => do
pure <| paramMap.insert (← param.fvarId!.getUserName) param
-- Collect autoParam tactics, which are all on the flat constructor:
let flatCtorName := mkFlatCtorOfStructName id
let autoParams : NameMap Syntax ← forallTelescope (← getConstInfo flatCtorName).type fun args _ =>
let flatCtorName := mkFlatCtorOfStructCtorName ctor
let flatCtorInfo ← try getConstInfo flatCtorName catch _ => getConstInfo (id ++ `_flat_ctor) -- TODO(kmill): remove catch
let autoParams : NameMap Syntax ← forallTelescope flatCtorInfo.type fun args _ =>
args[numParams:].foldlM (init := {}) fun set arg => do
let decl ← arg.fvarId!.getDecl
if let some (.const tacticDecl _) := decl.type.getAutoParamTactic? then
@ -187,7 +188,7 @@ private def printIdCore (id : Name) : CommandElabM Unit := do
| ConstantInfo.recInfo { levelParams := us, type := t, isUnsafe := u, .. } => printAxiomLike "recursor" id us t u
| ConstantInfo.inductInfo { levelParams := us, numParams, type := t, ctors, isUnsafe := u, .. } =>
if isStructure env id then
printStructure id us numParams t u
printStructure id us numParams t ctors[0]! u
else
printInduct id us numParams t ctors u
| none => throwUnknownId id

File diff suppressed because it is too large Load diff

View file

@ -510,46 +510,6 @@ private def reduceFieldProjs (e : Expr) (zetaDelta := true) : StructElabM Expr :
return TransformStep.continue
Meta.transform e (post := postVisit)
/-- Checks if the expression is of the form `S.mk x.1 ... x.n` with `n` nonzero
and `S.mk` a structure constructor with `S` one of the recorded structure parents.
Returns `x`.
Each projection `x.i` can be either a native projection or from a projection function. -/
private def etaStruct? (e : Expr) : StructElabM (Option Expr) := do
let .const f _ := e.getAppFn | return none
let some (ConstantInfo.ctorInfo fVal) := (← getEnv).find? f | return none
unless (← findParentFieldInfo? fVal.induct).isSome do return none
unless 0 < fVal.numFields && e.getAppNumArgs == fVal.numParams + fVal.numFields do return none
let args := e.getAppArgs
let some (S0, i0, x) ← getProjectedExpr args[fVal.numParams]! | return none
unless S0 == fVal.induct && i0 == 0 do return none
for i in [1 : fVal.numFields] do
let arg := args[fVal.numParams + i]!
let some (S', i', x') ← getProjectedExpr arg | return none
unless S' == fVal.induct && i' == i && x' == x do return none
return x
where
/-- Given an expression that's either a native projection or a registered projection
function, gives (1) the name of the structure type, (2) the index of the projection, and
(3) the object being projected. -/
getProjectedExpr (e : Expr) : MetaM (Option (Name × Nat × Expr)) := do
if let .proj S i x := e then
return (S, i, x)
if let .const fn _ := e.getAppFn then
if let some info ← getProjectionFnInfo? fn then
if e.getAppNumArgs == info.numParams + 1 then
if let some (ConstantInfo.ctorInfo fVal) := (← getEnv).find? info.ctorName then
return (fVal.induct, info.i, e.appArg!)
return none
/-- Runs `etaStruct?` over the whole expression. -/
private def etaStructReduce (e : Expr) : StructElabM Expr := do
let e ← instantiateMVars e
Meta.transform e (post := fun e => do
if let some e ← etaStruct? e then
return .done e
else
return .continue)
/--
Puts an expression into "field normal form".
- All projections of constructors for parent structures are reduced.
@ -557,7 +517,8 @@ Puts an expression into "field normal form".
- Constructors of parent structures are eta reduced.
-/
private def fieldNormalizeExpr (e : Expr) (zetaDelta : Bool := true) : StructElabM Expr := do
etaStructReduce <| ← reduceFieldProjs e (zetaDelta := zetaDelta)
let ancestors := (← get).ancestorFieldIdx
etaStructReduce (p := ancestors.contains) <| ← reduceFieldProjs e (zetaDelta := zetaDelta)
private def fieldFromMsg (info : StructFieldInfo) : MessageData :=
if let some sourceStructName := info.sourceStructNames.head? then
@ -1150,11 +1111,9 @@ Assumes the inductive type has already been added to the environment.
Note: we can't generally use optParams here since the default values might depend on previous ones.
We include autoParams however.
-/
private def mkFlatCtorExpr (levelParams : List Name) (params : Array Expr) (structName : Name) (replaceIndFVars : Expr → MetaM Expr) :
private def mkFlatCtorExpr (levelParams : List Name) (params : Array Expr) (ctor : ConstructorVal) (replaceIndFVars : Expr → MetaM Expr) :
StructElabM Expr := do
let env ← getEnv
-- build the constructor application using the fields in the local context
let ctor := getStructureCtor env structName
let mut val := mkAppN (mkConst ctor.name (levelParams.map mkLevelParam)) params
let fieldInfos := (← get).fields
for fieldInfo in fieldInfos do
@ -1173,17 +1132,20 @@ private def mkFlatCtorExpr (levelParams : List Name) (params : Array Expr) (stru
| _ => pure decl.type
let type ← zetaDeltaFVars (← instantiateMVars type) parentFVars
let type ← replaceIndFVars type
return .lam decl.userName type (val.abstract #[fieldInfo.fvar]) decl.binderInfo
return .lam decl.userName.eraseMacroScopes type (val.abstract #[fieldInfo.fvar]) decl.binderInfo
val ← mkLambdaFVars params val
val ← replaceIndFVars val
fieldNormalizeExpr val
private partial def mkFlatCtor (levelParams : List Name) (params : Array Expr) (structName : Name) (replaceIndFVars : Expr → MetaM Expr) :
StructElabM Unit := do
let val ← mkFlatCtorExpr levelParams params structName replaceIndFVars
let env ← getEnv
let ctor := getStructureCtor env structName
let val ← mkFlatCtorExpr levelParams params ctor replaceIndFVars
withLCtx {} {} do trace[Elab.structure] "created flat constructor:{indentExpr val}"
unless val.hasSyntheticSorry do
let flatCtorName := mkFlatCtorOfStructName structName
-- Note: flatCtorName will be private if the constructor is private
let flatCtorName := mkFlatCtorOfStructCtorName ctor.name
let valType ← replaceIndFVars (← instantiateMVars (← inferType val))
let valType := valType.inferImplicit params.size true
addDecl <| Declaration.defnDecl (← mkDefinitionValInferrringUnsafe flatCtorName levelParams valType val .abbrev)
@ -1393,8 +1355,8 @@ private def mkRemainingProjections (levelParams : List Name) (params : Array Exp
-- No need to zeta delta reduce; `fvarToConst` has replaced such fvars.
let val ← fieldNormalizeExpr val (zetaDelta := false)
fvarToConst := fvarToConst.insert field.fvar val
-- TODO(kmill): if it is a direct parent, add the coercion function the environment and use that instead of `val`,
-- and evaluate the difference.
-- TODO(kmill): if it is a direct parent, try adding the coercion function from the environment and use that instead of `val`.
-- (This should be evaluated to see if it is a good idea.)
else
throwError m!"(mkRemainingProjections internal error) {field.name} has no value"
@ -1465,30 +1427,31 @@ def elabStructureCommand : InductiveElabDescr where
collectUsedFVars := collectUsedFVars lctx localInsts fieldInfos
checkUniverses := fun _ u => withLCtx lctx localInsts do checkResultingUniversesForFields fieldInfos u
finalizeTermElab := withLCtx lctx localInsts do checkDefaults fieldInfos
prefinalize := fun _ _ _ => do
prefinalize := fun levelParams params replaceIndFVars => do
withLCtx lctx localInsts do
addProjections r fieldInfos
registerStructure view.declName fieldInfos
runStructElabM (init := state) do
mkFlatCtor levelParams params view.declName replaceIndFVars
addDefaults levelParams params replaceIndFVars
let parentInfos ← withLCtx lctx localInsts <| runStructElabM (init := state) do
mkRemainingProjections levelParams params view
setStructureParents view.declName parentInfos
withSaveInfoContext do -- save new env
for field in view.fields do
-- may not exist if overriding inherited field
if (← getEnv).contains field.declName then
Term.addTermInfo' field.ref (← mkConstWithLevelParams field.declName) (isBinder := true)
finalize := fun levelParams params replaceIndFVars => do
let parentInfos ← runStructElabM (init := state) <| withLCtx lctx localInsts <| mkRemainingProjections levelParams params view
withSaveInfoContext do
-- Add terminfo for parents now that all parent projections exist.
for parent in parents do
if parent.addTermInfo then
Term.addTermInfo' parent.ref (← mkConstWithLevelParams parent.declName) (isBinder := true)
setStructureParents view.declName parentInfos
checkResolutionOrder view.declName
if view.isClass then
addParentInstances parentInfos
runStructElabM (init := state) <| withLCtx lctx localInsts do
mkFlatCtor levelParams params view.declName replaceIndFVars
addDefaults levelParams params replaceIndFVars
return {
finalize := do
if view.isClass then
addParentInstances parentInfos
}
}
}

View file

@ -139,12 +139,14 @@ instance : ToString Fact where
/-- `tidy`, implemented on `Fact`. -/
def tidy (f : Fact) : Fact :=
match f.justification.tidy? with
| some ⟨_, _, justification⟩ => { justification }
| some ⟨constraint, coeffs, justification⟩ => { coeffs, constraint, justification }
| none => f
/-- `combo`, implemented on `Fact`. -/
def combo (a : Int) (f : Fact) (b : Int) (g : Fact) : Fact :=
{ justification := .combo a f.justification b g.justification }
{ coeffs := .combo a f.coeffs b g.coeffs
constraint := .combo a f.constraint b g.constraint
justification := .combo a f.justification b g.justification }
end Fact

View file

@ -109,4 +109,69 @@ def mkProjections (n : Name) (projDecls : Array StructProjDecl) (instImplicit :
let proj := mkApp (mkAppN (.const projName lvls) params) self
ctorType := ctorType.bindingBody!.instantiate1 proj
/--
Checks if the expression is of the form `S.mk x.1 ... x.n` with `n` nonzero
and `S.mk` a structure constructor with `S` one of the recorded structure parents.
Returns `x`.
Each projection `x.i` can be either a native projection or from a projection function.
-/
def etaStruct? (e : Expr) (p : Name → Bool) : MetaM (Option Expr) := do
let .const ctor _ := e.getAppFn | return none
let some (ConstantInfo.ctorInfo fVal) := (← getEnv).find? ctor | return none
unless p fVal.induct do return none
unless 0 < fVal.numFields && e.getAppNumArgs == fVal.numParams + fVal.numFields do return none
let args := e.getAppArgs
let params := args.extract 0 fVal.numParams
let some x ← getProjectedExpr ctor fVal.induct params 0 args[fVal.numParams]! none | return none
for i in [1 : fVal.numFields] do
let arg := args[fVal.numParams + i]!
let some x' ← getProjectedExpr ctor fVal.induct params i arg x | return none
unless x' == x do return none
return x
where
sameParams (params1 params2 : Array Expr) : MetaM Bool := withNewMCtxDepth do
if params1.size == params2.size then
for p1 in params1, p2 in params2 do
unless ← isDefEqGuarded p1 p2 do
return false
return true
else
return false
/--
Given an expression `e` that's either a native projection or a registered projection
function, gives the object being projected.
Checks that the parameters are defeq to `params`, that the projection index is equal to `idx`,
and, if `x?` is provided, that the object being projected is equal to it.
-/
getProjectedExpr (ctor induct : Name) (params : Array Expr) (idx : Nat) (e : Expr) (x? : Option Expr) : MetaM (Option Expr) := do
if let .proj S i x := e then
if i == idx && induct == S && (x? |>.map (· == x) |>.getD true) then
let ety ← whnf (← inferType e)
let params' := ety.getAppArgs
if ← sameParams params params' then
return x
return none
if let .const fn _ := e.getAppFn then
if let some info ← getProjectionFnInfo? fn then
if info.ctorName == ctor && info.i == idx && e.getAppNumArgs == info.numParams + 1 then
let x := e.appArg!
if (x? |>.map (· == x) |>.getD true) then
let params' := e.appFn!.getAppArgs
if ← sameParams params params' then
return e.appArg!
return none
/--
Eta reduces all structures satisfying `p` in the whole expression.
See `etaStruct?` for reducing single expressions.
-/
def etaStructReduce (e : Expr) (p : Name → Bool) : MetaM Expr := do
let e ← instantiateMVars e
Meta.transform e (post := fun e => do
if let some e ← etaStruct? e p then
return .done e
else
return .continue)
end Lean.Meta

View file

@ -6,6 +6,7 @@ Authors: Leonardo de Moura
Helper functions for retrieving structure information.
-/
prelude
import Init.Control.Option
import Lean.Environment
import Lean.ProjFns
import Lean.Exception
@ -98,7 +99,7 @@ structure StructureDescr where
deriving Inhabited
/--
Declare a new structure to the elaborator.
Declares a new structure to the elaborator.
Every structure created by `structure` or `class` has such an entry.
This should be followed up with `setStructureParents` and `setStructureResolutionOrder`.
-/
@ -110,7 +111,7 @@ def registerStructure (env : Environment) (e : StructureDescr) : Environment :=
}
/--
Set parent projection info for a structure defined in the current module.
Sets parent projection info for a structure defined in the current module.
Throws an error if the structure has not already been registered with `Lean.registerStructure`.
-/
def setStructureParents [Monad m] [MonadEnv m] [MonadError m] (structName : Name) (parentInfo : Array StructureParentInfo) : m Unit := do
@ -154,26 +155,29 @@ def getStructureCtor (env : Environment) (constName : Name) : ConstructorVal :=
def getStructureFields (env : Environment) (structName : Name) : Array Name :=
(getStructureInfo env structName).fieldNames
/-- Get the `StructureFieldInfo` for the given direct field of the structure. -/
/-- Gets the `StructureFieldInfo` for the given direct field of the structure. -/
def getFieldInfo? (env : Environment) (structName : Name) (fieldName : Name) : Option StructureFieldInfo :=
if let some info := getStructureInfo? env structName then
info.fieldInfo.binSearch { fieldName := fieldName, projFn := default, subobject? := none, binderInfo := default } StructureFieldInfo.lt
else
none
/-- If `fieldName` is a subobject (that it, if it is an embedded parent structure), then returns the name of that parent structure. -/
/--
If `fieldName` is a subobject (that it, if it is an embedded parent structure),
then returns the name of that parent structure.
-/
def isSubobjectField? (env : Environment) (structName : Name) (fieldName : Name) : Option Name :=
if let some fieldInfo := getFieldInfo? env structName fieldName then
fieldInfo.subobject?
else
none
/-- Get information for all the parents that appear in the `extends` clause. -/
/-- Gets information for all the parents that appear in the `extends` clause. -/
def getStructureParentInfo (env : Environment) (structName : Name) : Array StructureParentInfo :=
(getStructureInfo env structName).parentInfo
/--
Return the parent structures that are embedded in the structure.
Returns the parent structures that are embedded in the structure.
This is the array of all results from `Lean.isSubobjectField?` in order.
Note: this is *not* a subset of the parents from `getStructureParentInfo`.
@ -184,14 +188,37 @@ def getStructureSubobjects (env : Environment) (structName : Name) : Array Name
(getStructureFields env structName).filterMap (isSubobjectField? env structName)
/--
Return the name of the structure that contains the field relative to structure `structName`.
Returns the name of the structure that contains the field relative to structure `structName`.
If `structName` contains the field itself, returns that,
and otherwise recursively looks into parents that are subobjects. -/
and otherwise recursively looks into parents that are subobjects.
-/
partial def findField? (env : Environment) (structName : Name) (fieldName : Name) : Option Name :=
if (getStructureFields env structName).contains fieldName then
some structName
else
getStructureSubobjects env structName |>.findSome? fun parentStructName => findField? env parentStructName fieldName
getStructureSubobjects env structName |>.findSome? (findField? env · fieldName)
/--
Given a structure `structName` and a parent projection name `projName` (e.g. `toParentStructName`),
returns the corresponding parent structure name.
The parent projection name is a single-component name.
Note: this relies on the fact that projection names are checked to be consistent across all parents.
-/
partial def findParentProjStruct? (env : Environment) (structName : Name) (projName : Name) : Option Name :=
go structName |>.run' {}
where
-- Use a cache to navigate the DAG in polynomial time
go (structName : Name) : StateM NameSet (Option Name) := do
if (← get).contains structName then
return none
else
let parentInfos := getStructureParentInfo env structName
if let some parentInfo := parentInfos.find? (projName.isSuffixOf ·.projFn) then
return some parentInfo.structName
else
modify fun s => s.insert structName
parentInfos.findSomeM? (go ·.structName)
/--
Gets the name for a structure constructor where the fields have been fully flattened.
@ -204,8 +231,8 @@ The body of the flat constructor has the following properties (recursively):
- for subobject fields, the value is the unfolded flat constructor for that field
- for standard fields, the value is one of the flat constructor parameters
-/
def mkFlatCtorOfStructName (structName : Name) : Name :=
structName ++ `_flat_ctor
def mkFlatCtorOfStructCtorName (structCtorName : Name) : Name :=
structCtorName ++ `_flat_ctor
private partial def getStructureFieldsFlattenedAux (env : Environment) (structName : Name) (fullNames : Array Name) (includeSubobjectFields : Bool) : Array Name :=
(getStructureFields env structName).foldl (init := fullNames) fun fullNames fieldName =>
@ -302,30 +329,27 @@ The effective autoParams are collected in the flat constructor.
def getAutoParamFnForField? (env : Environment) (structName : Name) (fieldName : Name) : Option Name :=
getFnForFieldUsing? mkAutoParamFnOfProjFn env structName fieldName
partial def getPathToBaseStructureAux (env : Environment) (baseStructName : Name) (structName : Name) (path : List Name) : Option (List Name) :=
if baseStructName == structName then
some path.reverse
else
if let some info := getStructureInfo? env structName then
-- Prefer subobject projections
(info.fieldInfo.findSome? fun field =>
match field.subobject? with
| none => none
| some parentStructName => getPathToBaseStructureAux env baseStructName parentStructName (field.projFn :: path))
-- Otherwise, consider other parents
<|> info.parentInfo.findSome? fun parent =>
if parent.subobject then
none
else
getPathToBaseStructureAux env baseStructName parent.structName (parent.projFn :: path)
else none
/--
If `baseStructName` is an ancestor structure for `structName`, then return a sequence of projection functions
If `baseStructName` is an ancestor structure for `structName`, then returns a sequence of projection functions
to go from `structName` to `baseStructName`. Returns `[]` if `baseStructName == structName`.
-/
def getPathToBaseStructure? (env : Environment) (baseStructName : Name) (structName : Name) : Option (List Name) :=
getPathToBaseStructureAux env baseStructName structName []
partial def getPathToBaseStructure? (env : Environment) (baseStructName : Name) (structName : Name) : Option (List Name) :=
OptionT.run (go structName []) |>.run' {}
where
go (structName : Name) (path : List Name) : OptionT (StateM NameSet) (List Name) := do
if baseStructName == structName then
return path.reverse
else
guard <| !(← get).contains structName
modify fun s => s.insert structName
let some info := getStructureInfo? env structName | failure
-- Prefer subobject projections
(info.fieldInfo.firstM fun field => do
let some parentStructName := field.subobject? | failure
go parentStructName (field.projFn :: path))
-- Otherwise, consider other parents
<|> info.parentInfo.firstM fun parent => do
go parent.structName (parent.projFn :: path)
/--
Returns true iff `constName` is a non-recursive inductive datatype that has only one constructor and no indices.
@ -348,7 +372,7 @@ def getStructureLikeCtor? (env : Environment) (constName : Name) : Option Constr
| _ => panic! "ill-formed environment"
| _ => none
/-- Return number of fields for a structure-like type -/
/-- Returns the number of fields for a structure-like type -/
def getStructureLikeNumFields (env : Environment) (constName : Name) : Nat :=
match env.find? constName with
| some (.inductInfo { isRec := false, ctors := [ctor], numIndices := 0, .. }) =>

View file

@ -430,6 +430,7 @@ def loadTomlConfig (cfg: LoadConfig) : LogIO Package := do
let defaultTargets := defaultTargets.map stringToLegalOrSimpleName
let depConfigs ← table.tryDecodeD `require #[]
return {
name := name
dir := cfg.pkgDir
relDir := cfg.relPkgDir
relConfigFile := cfg.relConfigFile

View file

@ -50,7 +50,7 @@ def tst : MetaM Unit :=
example (z : A) : z.x = 1 := by
match z with
| { a' := h } => trace_state; exact h
| { a' := h, .. } => trace_state; exact h
example (z : A) : z.x = 1 := by
match z with

View file

@ -16,7 +16,7 @@ a' : x = 1
⊢ { x := x, a' := a' }.x = 1
case mk
x y : Nat
⊢ { x := x, y := y } = { x := { x := x, y := y }.x, y := { x := x, y := y }.y }
⊢ { x := x, y := y } = { x := x, y := y }
a.1 = 1
z : A
x✝ : Nat

View file

@ -14,7 +14,8 @@ structure Foo1 (α : Type) extends Bar (αα), Baz α
#check Foo1.mk
def f1 (x : Nat) : Foo1 Nat :=
{ a := id
{ β := _
a := id
x := (· + ·)
b := fun _ => "" }
@ -29,7 +30,8 @@ structure Foo2 (α : Type) extends Bar (αα), Boo2 α
#check Foo2.mk
def f2 (v : Nat) : Foo2 Nat :=
{ a := id
{ β := _
a := id
x := (· + ·)
b := fun _ => ""
x1 := 1

View file

@ -1,25 +0,0 @@
import Lean
structure A where
x : Nat := 0
structure B extends A where
y : Nat := 0
structure C extends B where
z : Nat := 0
def c : C := {}
open Lean
open Lean.Elab
def tst (varName structName fieldName : Name) : TermElabM Unit := do
let c := mkIdent varName
let some p ← Lean.Elab.Term.StructInst.mkProjStx? c structName fieldName | throwError "failed"
let p ← Term.elabTerm p none
logInfo s!"{p}"
#eval tst `c `C `x
#eval tst `c `C `y
#eval tst `c `C `z

View file

@ -1,3 +0,0 @@
A.x (B.toA (C.toB c))
B.y (C.toB c)
C.z c

View file

@ -145,11 +145,13 @@ instance PartialOrder {ι : Type u} {α : ι → Type v} [∀ i, PartialOrder (
le_antisymm := sorry }
instance semilatticeSup [∀ i, SemilatticeSup (α' i)] : SemilatticeSup (∀ i, α' i) where
sup x y i := x i ⊔ y i
le_sup_left _ _ _ := SemilatticeSup.le_sup_left _ _
le_sup_right _ _ _ := SemilatticeSup.le_sup_right _ _
sup_le _ _ _ ac bc i := SemilatticeSup.sup_le _ _ _ (ac i) (bc i)
instance semilatticeInf [∀ i, SemilatticeInf (α' i)] : SemilatticeInf (∀ i, α' i) where
inf x y i := x i ⊓ y i
inf_le_left _ _ _ := SemilatticeInf.inf_le_left _ _
inf_le_right _ _ _ := SemilatticeInf.inf_le_right _ _
le_inf _ _ _ ac bc i := SemilatticeInf.le_inf _ _ _ (ac i) (bc i)

View file

@ -15,8 +15,23 @@ structure Subalgebra [OneHom R A] : Type extends Subone A where
algebraMap_mem : ∀ r : R, mem (OneHom.toFun r)
one_mem := OneHom.map_one (R := R) (A := A) ▸ algebraMap_mem One.one
set_option pp.mvars false
/--
error: fields missing: 'one_mem'
error: don't know how to synthesize placeholder
context:
R A : Type
inst✝² : One R
inst✝¹ : One A
inst✝ : OneHom R A
⊢ ∀ (r : R), ?_ R A _example (OneHom.toFun r)
---
error: don't know how to synthesize placeholder
context:
R A : Type
inst✝² : One R
inst✝¹ : One A
inst✝ : OneHom R A
⊢ A → Prop
-/
#guard_msgs in
example [OneHom R A] : Subalgebra R A where

View file

@ -522,9 +522,11 @@ namespace Pi
variable {ι : Type _} {α' : ι → Type _}
instance instOrderTop [∀ i, LE (α' i)] [∀ i, OrderTop (α' i)] : OrderTop (∀ i, α' i) where
top := fun _ =>
le_top _ := fun _ => le_top
instance instOrderBot [∀ i, LE (α' i)] [∀ i, OrderBot (α' i)] : OrderBot (∀ i, α' i) where
bot := fun _ => ⊥
bot_le _ := fun _ => bot_le
end Pi
@ -2519,7 +2521,7 @@ variable {L : Type _} [Field L] [Algebra F L] [Algebra L E] [IsScalarTower F L E
(f : L →ₐ[F] K)
-- This only required 16,000 heartbeats prior to #3807, and now takes ~210,000.
set_option maxHeartbeats 20000
set_option maxHeartbeats 16000
theorem exists_algHom_adjoin_of_splits''' :
∃ φ : adjoin L S →ₐ[F] K, φ.comp (IsScalarTower.toAlgHom F L _) = f := by
let L' := (IsScalarTower.toAlgHom F L E).fieldRange
@ -2531,7 +2533,12 @@ theorem exists_algHom_adjoin_of_splits''' :
· simp only [← SetLike.coe_subset_coe, coe_restrictScalars, adjoin_subset_adjoin_iff]
exact ⟨subset_adjoin_of_subset_left S (F := L'.toSubfield) le_rfl, subset_adjoin _ _⟩
· ext x
exact (congrFun (congrArg (fun g : L' →ₐ[F] K => (g : L' → K)) hφ) _).trans (congrArg f <| AlgEquiv.symm_apply_apply _ _)
exact
Eq.trans
(congrFun (congrArg (fun g : L' →ₐ[F] K => (g : L' → K)) hφ)
(DFunLike.coe (AlgEquiv.ofInjectiveField _) x))
(congrArg f
(AlgEquiv.symm_apply_apply (AlgEquiv.ofInjectiveField (IsScalarTower.toAlgHom F L E)) x))
end IntermediateField

View file

@ -6,6 +6,8 @@ class MyClass (α : Type u) extends LE α where
sup_of_le_left : ∀ a b : α, b ≤ a → sup a b = a
instance : MyClass Prop where
le p q := p → q
sup p q := p q
le_refl _ := id
sup_of_le_left _ _ h := propext ⟨Or.rec id h, Or.inl⟩

View file

@ -10,5 +10,10 @@ structure BarS extends FooS where
def f (x : Nat) : BarS :=
{ x, y := x, h' := rfl }
/-- error: cannot synthesize placeholder for field 'h' -/
#guard_msgs in
example (x : Nat) : BarS :=
{ x, h' := rfl, .. }
def f1 (x : Nat) : BarS :=
{ x, h' := rfl }
{ x, h' := rfl, y := _ }

View file

@ -6,16 +6,17 @@ This is to fix a bug where structure instance notation was not working when ther
/-!
Motivating issue from https://github.com/leanprover/lean4/issues/5406
The `example` had an elaboration error because the structure instance was expanding to `{b := m.b}`.
Now it expands to `{b := @m.b}`.
The `example` had an elaboration error because the structure instance was expanding to `{b := m.b, x := 1}`.
Now it expands to `{b := @m.b, x := 1}`.
-/
structure Methods where
b : Nat → (opt : Nat := 42) → Nat
x : Nat
example (m : Methods) : Methods := { m with }
example (m : Methods) : Methods := { m with x := 1 }
/-- info: fun m => { b := @Methods.b m } : Methods → Methods -/
#guard_msgs in #check fun (m : Methods) => { m with }
/-- info: fun m => { b := @Methods.b m, x := 1 } : Methods → Methods -/
#guard_msgs in #check fun (m : Methods) => { m with x := 1 }
/-!
@ -54,6 +55,7 @@ We need this so that structure instances work properly.
class C (α : Type) [Inhabited α] where
f (x : α := default) : α
x : Nat
/-- info: fun inst => C.f : C Nat → Nat -/
#guard_msgs in #check fun (inst : C Nat) => inst.f
@ -64,8 +66,8 @@ class C (α : Type) [Inhabited α] where
/-- info: fun inst => @C.f Nat instInhabitedNat inst : C Nat → optParam Nat default → Nat -/
#guard_msgs in #check fun (inst : C Nat) => @C.f _ _ inst
/-- info: fun inst => { f := @C.f Nat instInhabitedNat inst } : C Nat → C Nat -/
#guard_msgs in #check fun (inst : C Nat) => { inst with }
/-- info: fun inst => { f := @C.f Nat instInhabitedNat inst, x := 1 } : C Nat → C Nat -/
#guard_msgs in #check fun (inst : C Nat) => { inst with x := 1 }
/-!
@ -93,7 +95,8 @@ Tests of implicit arguments in updates.
structure I where
f : {_ : Nat} → Nat
x := 1
-- used to give `fun i ↦ ?m.369 i : I → I`
/-- info: fun i => { f := @I.f i } : I → I -/
#guard_msgs in #check fun (i : I) => {i with}
#guard_msgs in #check fun (i : I) => {i with x := 1 }

View file

@ -338,7 +338,6 @@ end Cat
def typeToCat : Type u ⥤ Cat where
obj X := Cat.of (Discrete X)
map := fun {X} {Y} f => by
dsimp
exact Discrete.functor (Discrete.mk ∘ f)
@[simp] theorem typeToCat_obj (X : Type u) : typeToCat.obj X = Cat.of (Discrete X) := rfl

View file

@ -13,8 +13,7 @@ set_option pp.explicit true
info: def foo : Foo :=
{ obj := fun x => @Function.const Type (@Eq Unit Unit.unit Unit.unit) Nat foo.proof_1,
map :=
@id ((fun x => @Function.const Type (@Eq Unit Unit.unit Unit.unit) Nat foo.proof_1) Unit.unit)
(@OfNat.ofNat Nat 0 (instOfNatNat 0)) }
@id (@Function.const Type (@Eq Unit Unit.unit Unit.unit) Nat foo.proof_1) (@OfNat.ofNat Nat 0 (instOfNatNat 0)) }
-/
#guard_msgs in
#print foo

View file

@ -265,17 +265,14 @@ instance Functor.category : Category.{max u₁ v₂} (C ⥤ D) where
comp α β := vcomp α β
id_comp := by
intro X Y f
simp_all only
ext x : 2
apply id_comp
comp_id := by
intro X Y f
simp_all only
ext x : 2
apply comp_id
assoc := by
intro W X Y Z f g h
simp_all only
ext x : 2
apply assoc
@ -384,7 +381,6 @@ instance functorCategoryPreadditive : Preadditive (C ⥤ D) where
apply add_zero
neg_add_cancel := by
intros
dsimp only
ext
apply neg_add_cancel }
add_comp := by

View file

@ -129,6 +129,7 @@ end Mathlib.Algebra.Ring.Defs
section Mathlib.Data.Int.Basic
instance : CommRing Int where
one := 1
mul_comm := sorry
mul_one := Int.mul_one -- Replacing this with `sorry` makes the timeout go away!
add_zero := Int.add_zero -- Similarly here.

View file

@ -122,7 +122,7 @@ example : 6 = 6 ∧ [7] = [7] := by
-- Test that `Config.intros` causes `solve_by_elim` to call `intro` on intermediate goals.
example (P : Prop) : P → P := by
fail_if_success solve_by_elim (config := {intros := false})
fail_if_success solve_by_elim (config := {intro := false})
solve_by_elim
-- This worked in mathlib3 without the `@`, but now goes into a loop.

View file

@ -58,13 +58,21 @@ structure B extends A where
structure C extends B where
(z : Nat := 2*y) (x := z + 2) (y := z + 3)
/-- info: { x := 1, y := 1 + 2, z := 2 * (1 + 2) } : C -/
-- This first example does not work because the default values at `C` are the only ones considered.
/--
error: fields missing: 'y', 'z'
---
info: { x := 1, y := sorry, z := sorry } : C
-/
#guard_msgs in #check { x := 1 : C }
/-- info: { x := 2 * 1 + 2, y := 1, z := 2 * 1 } : C -/
#guard_msgs in #check { y := 1 : C }
/-- info: { x := 1 + 2, y := 1 + 3, z := 1 } : C -/
#guard_msgs in #check { z := 1 : C }
-- This first example does not work because the default values at `C` are the only ones considered.
/-- error: fields missing: 'y', 'z' -/
#guard_msgs in
def test1 : C where
x := 1
def test2 : C where
@ -159,3 +167,154 @@ def test1 (z : Int) : A Int Int where
z
end Ex5
/-!
Default instances are applied before analyzing default values.
Without this, `α` would be reported as being a missing field.
-/
namespace Ex6
structure MyStruct where
{α : Type u}
{β : Type v}
a : α
b : β
/-- info: { α := Nat, β := Bool, a := 10, b := true } : MyStruct -/
#guard_msgs in #check { a := 10, b := true : MyStruct }
end Ex6
/-!
Make sure we have the Lean 3 behavior, where field projections are unfolded.
https://github.com/leanprover-community/mathlib4/issues/12129#issuecomment-2056134533
-/
namespace Mathlib12129
structure Foo where
toFun : Nat → Nat
structure Bar extends Foo where
prop : toFun 0 = 0
/-
Rather than `(fun x => x) 0 = 0` or `{ toFun := fun x => x }.toFun 0 = 0`
-/
/-- info: ⊢ 0 = 0 -/
#guard_msgs in
def bar : Bar where
toFun x := x
prop := by
trace_state
rfl
end Mathlib12129
/-!
Explicit fields must be provided, even if they can be inferred.
-/
namespace Ex7
structure S where
n : Nat
m : Fin n
variable (x : Fin 3)
/--
error: fields missing: 'n'
field 'n' must be explicitly provided, its synthesized value is
3
---
info: { n := 3, m := x } : S
-/
#guard_msgs in #check { m := x : S }
/-- info: { n := 3, m := x } : S -/
#guard_msgs in #check { n := _, m := x : S }
/-- info: { n := 3, m := x } : S -/
#guard_msgs in #check { m := x, .. : S }
/-- info: { n := 3, m := x } : S -/
#guard_msgs in #check { n := 3, m := x : S }
end Ex7
/-!
Diamond inheritance, acquire field values from parent classes.
https://github.com/leanprover/lean4/issues/6046
-/
namespace Issue6046
set_option structure.strictResolutionOrder true
class A
class B extends A where
b : Unit
structure B' where
b : Unit
class C extends B', B, A
instance : A where
instance : B where
b := ()
instance : C where
/--
info: def Issue6046.instC : C :=
{ b := B.b }
-/
#guard_msgs in #print Issue6046.instC
end Issue6046
/-!
Make sure parent fields still work if one parent depends on another.
-/
namespace Ex8
class A (α : Type) where
val : α → Nat
class B (α : Type) [A α] where
val' : α → Nat
h : ∃ x : α, A.val x = val' x
class C (α : Type) extends A α, B α
instance : A Nat where
val := id
instance : B Nat where
val' _ := 0
h := by exists 0
/-
This was "fields missing: 'val'', 'h'" at some point during testing.
To succeed, this relies on being able to compute the type of the `B` parent,
which depends on fields of the structure instance being elaborated.
-/
/-- info: { toA := instANat, toB := instBNat } : C Nat -/
#guard_msgs in #check { : C Nat }
end Ex8
/-!
Autoparams are elaborated eagerly. Here we see that `a` is printed before `b`.
-/
namespace Ex9
structure A where
n : Nat := by trace "a"; exact 1
m : Fin n
/--
info: a
---
info: b
-/
#guard_msgs in
example : A where
m := by trace "b"; exact 0
end Ex9

View file

@ -1,16 +1,18 @@
universe u
set_option pp.structureInstanceTypes true
namespace Ex1
structure A (α : Type u) :=
(x : α) (f : αα := λ x => x)
structure A (α : Type u) where
(x : α) (f : αα := λ x => x)
structure B (α : Type u) extends A α :=
(y : α := f (f x)) (g : ααα := λ x y => f x)
structure B (α : Type u) extends A α where
(y : α := f (f x)) (g : ααα := λ x y => f x)
structure C (α : Type u) extends B α :=
(z : α := g x y) (x := f z)
structure C (α : Type u) extends B α where
(z : α := g x y) (x := f z)
end Ex1
@ -18,24 +20,60 @@ open Ex1
def c1 : C Nat := { x := 1 }
#check { c1 with z := 2 }
#check { c1 with z := 2 }
/--
info: let __src := c1;
{ toB := __src.toB, z := 2 : C Nat } : C Nat
-/
#guard_msgs in #check { c1 with z := 2 }
theorem ex1 : { c1 with z := 2 }.z = 2 :=
rfl
#check ex1
/--
info: ex1 :
(let __src := c1;
{ toB := __src.toB, z := 2 : C Nat }).z =
2
-/
#guard_msgs in #check ex1
theorem ex2 : { c1 with z := 2 }.x = c1.x :=
rfl
#check ex2
/--
info: ex2 :
(let __src := c1;
{ toB := __src.toB, z := 2 : C Nat }).x =
c1.x
-/
#guard_msgs in #check ex2
def c2 : C (Nat × Nat) := { z := (1, 1) }
#check { c2 with x.fst := 2 }
/--
info: let __src := c2;
{
x :=
let __src := __src.x;
(2, __src.snd),
f := __src.f, y := __src.y, g := __src.g, z := __src.z : C (Nat × Nat) } : C (Nat × Nat)
-/
#guard_msgs in #check { c2 with x.fst := 2 }
#check { c2 with x.1 := 3 }
/--
info: let __src := c2;
{
x :=
let __src := __src.x;
(3, __src.snd),
f := __src.f, y := __src.y, g := __src.g, z := __src.z : C (Nat × Nat) } : C (Nat × Nat)
-/
#guard_msgs in #check { c2 with x.1 := 3 }
#check show C _ from { c2.toB with .. }
/--
info: let_fun this :=
let __src := c2.toB;
{ toB := __src, z := __src.g __src.x __src.y : C (Nat × Nat) };
this : C (Nat × Nat)
-/
#guard_msgs in #check show C _ from { c2.toB with .. }

View file

@ -3,23 +3,88 @@ universe u
def a : Array ((Nat × Nat) × Bool) := #[]
def b : Array Nat := #[]
structure Foo :=
(x : Array ((Nat × Nat) × Bool) := #[])
(y : Nat := 0)
structure Foo where
(x : Array ((Nat × Nat) × Bool) := #[])
(y : Nat := 0)
#check (b).modifyOp (idx := 1) (fun s => 2)
/-- info: b.modifyOp 1 fun _s => 2 : Array Nat -/
#guard_msgs in #check (b).modifyOp (idx := 1) (fun _s => 2)
#check { b with [1] := 2 }
/--
info: let __src := b;
__src.modifyOp 1 fun s => 2 : Array Nat
-/
#guard_msgs in #check { b with [1] := 2 }
#check { a with [1].fst.2 := 1 }
/--
info: let __src := a;
__src.modifyOp 1 fun s =>
(let __src := s.fst;
(__src.fst, 1),
s.snd) : Array ((Nat × Nat) × Bool)
-/
#guard_msgs in #check { a with [1].fst.2 := 1 }
def foo : Foo := {}
#check foo.x[1]!.1.2
/-- info: foo.x[1]!.fst.snd : Nat -/
#guard_msgs in #check foo.x[1]!.1.2
#check { foo with x[1].2 := true }
#check { foo with x[1].fst.snd := 1 }
#check { foo with x[1].1.fst := 1 }
/--
info: let __src := foo;
{
x :=
let __src := __src.x;
__src.modifyOp 1 fun s => (s.fst, true),
y := __src.y } : Foo
-/
#guard_msgs in #check { foo with x[1].2 := true }
/--
info: let __src := foo;
{
x :=
let __src := __src.x;
__src.modifyOp 1 fun s =>
(let __src := s.fst;
(__src.fst, 1),
s.snd),
y := __src.y } : Foo
-/
#guard_msgs in #check { foo with x[1].fst.snd := 1 }
/--
info: let __src := foo;
{
x :=
let __src := __src.x;
__src.modifyOp 1 fun s =>
(let __src := s.fst;
(1, __src.snd),
s.snd),
y := __src.y } : Foo
-/
#guard_msgs in #check { foo with x[1].1.fst := 1 }
#check { foo with x[1].1.1 := 5 }
#check { foo with x[1].1.2 := 5 }
/--
info: let __src := foo;
{
x :=
let __src := __src.x;
__src.modifyOp 1 fun s =>
(let __src := s.fst;
(5, __src.snd),
s.snd),
y := __src.y } : Foo
-/
#guard_msgs in #check { foo with x[1].1.1 := 5 }
/--
info: let __src := foo;
{
x :=
let __src := __src.x;
__src.modifyOp 1 fun s =>
(let __src := s.fst;
(__src.fst, 5),
s.snd),
y := __src.y } : Foo
-/
#guard_msgs in #check { foo with x[1].1.2 := 5 }

View file

@ -18,10 +18,25 @@ structure D extends B
def a : A := ⟨ 0 ⟩
def b : B := { a with }
#print b
/--
info: def b : B :=
let __src := a;
{ toA := __src }
-/
#guard_msgs in #print b
def c : C := { a with }
#print c
/--
info: def c : C :=
let __src := a;
{ toB := { toA := __src } }
-/
#guard_msgs in #print c
def d : D := { c with }
#print d
/--
info: def d : D :=
let __src := c;
{ toB := __src.toB }
-/
#guard_msgs in #print d

View file

@ -48,7 +48,7 @@ def baseTypeIdent := mkIdent (Name.mkSimple "Base")
def init (type val : TSyntax `ident) (width : Nat) : MacroM (TSyntax `command) := do
let fieldsStx ← mkFieldsStx type "base_" width
let vals := Array.mkArray width val
let vals := Array.replicate width val
`(structure $baseTypeIdent where
$fieldsStx:structFields
def $baseIdent : $baseTypeIdent := ⟨$vals,*⟩)

View file

@ -0,0 +1,17 @@
structure A where
x : Nat := 0
structure B extends A where
y : Nat := 0
structure C extends B where
z : Nat := 0
variable (c : C)
/-- info: { x := 1, y := c.y, z := c.z } : C -/
#guard_msgs in #check { c with x := 1 }
/-- info: { toA := c.toA, y := 1, z := c.z } : C -/
#guard_msgs in #check { c with y := 1 }
/-- info: { toB := c.toB, z := 1 } : C -/
#guard_msgs in #check { c with z := 1 }

View file

@ -122,3 +122,15 @@ field notation resolution order:
S2', S, U
-/
#guard_msgs in #print S2'
/-!
Structure instances, setting a parent
-/
/-- info: { toTheS := s, y := 1 } : S2' -/
#guard_msgs in variable (s : S) in #check { toTheS := s, y := 1 : S2' }
/-- info: { x := s.x, y := 1 } : S2' -/
#guard_msgs in variable (s : U) in #check { toTheU := s, y := 1 : S2' }
/-- info: { x := 2, y := 1 } : S2' -/
#guard_msgs in #check { toTheS.x := 2, y := 1 : S2' }
/-- info: { x := 2, y := 1 } : S2' -/
#guard_msgs in #check { toTheU.x := 2, y := 1 : S2' }

View file

@ -1,10 +1,13 @@
#check { fst := 10, snd := 20 : Nat × Nat }
/-- info: (10, 20) : Nat × Nat -/
#guard_msgs in #check { fst := 10, snd := 20 : Nat × Nat }
structure S :=
(x : Nat) (y : Bool) (z : String)
structure S where
(x : Nat) (y : Bool) (z : String)
#check { x := 10, y := true, z := "hello" : S }
/-- info: { x := 10, y := true, z := "hello" } : S -/
#guard_msgs in #check { x := 10, y := true, z := "hello" : S }
#check { fst := "hello", snd := "world" : Prod _ _ }
/-- info: ("hello", "world") : String × String -/
#guard_msgs in #check { fst := "hello", snd := "world" : Prod _ _ }

View file

@ -34,27 +34,27 @@ structure S4 (α : Type) extends S2 α, S3 α where
/-- info: Test1.S4.mk {α : Type} (toS2 : S2 α) (w : Nat) (x' : α) : S4 α -/
#guard_msgs in #check S4.mk
/--
info: def Test1.S1._flat_ctor : {α : Type} → α → Nat → S1 α :=
info: def Test1.S1.mk._flat_ctor : {α : Type} → α → Nat → S1 α :=
fun α x y => S1.mk x y
-/
#guard_msgs in #print S1._flat_ctor
#guard_msgs in #print S1.mk._flat_ctor
/--
info: def Test1.S2._flat_ctor : {α : Type} → α → Nat → Nat → S2 α :=
info: def Test1.S2.mk._flat_ctor : {α : Type} → α → Nat → Nat → S2 α :=
fun α x y z => S2.mk (S1.mk x y) z
-/
#guard_msgs in #print S2._flat_ctor
#guard_msgs in #print S2.mk._flat_ctor
/--
info: def Test1.S3._flat_ctor : {α : Type} → α → Nat → Nat → S3 α :=
info: def Test1.S3.mk._flat_ctor : {α : Type} → α → Nat → Nat → S3 α :=
fun α x y w => S3.mk (S1.mk x y) w
-/
#guard_msgs in #print S3._flat_ctor
#guard_msgs in #print S3.mk._flat_ctor
/--
info: def Test1.S4._flat_ctor : {α : Type} → α → Nat → Nat → Nat → α → S4 α :=
info: def Test1.S4.mk._flat_ctor : {α : Type} → α → Nat → Nat → Nat → α → S4 α :=
fun α x y z w x' => S4.mk (S2.mk (S1.mk x y) z) w x'
-/
#guard_msgs in #print S4._flat_ctor
/-- info: Test1.S4._flat_ctor {α : Type} (x : α) (y z w : Nat) (x' : α) : S4 α -/
#guard_msgs in #check S4._flat_ctor
#guard_msgs in #print S4.mk._flat_ctor
/-- info: Test1.S4.mk._flat_ctor {α : Type} (x : α) (y z w : Nat) (x' : α) : S4 α -/
#guard_msgs in #check S4.mk._flat_ctor
end Test1
@ -146,16 +146,16 @@ info: Test2_1.Semigroup.mk.{u_1} {α : Type u_1} [toMul : Mul α] [toAssociative
-/
#guard_msgs in #check Semigroup.mk
/--
info: def Test2_1.Semigroup._flat_ctor.{u_1} : {α : Type u_1} →
info: def Test2_1.Semigroup.mk._flat_ctor.{u_1} : {α : Type u_1} →
(mul : ααα) → (∀ (x y z : α), @Eq α (mul (mul x y) z) (mul x (mul y z))) → Semigroup α :=
fun α mul mul_assoc => @Semigroup.mk α (@Mul.mk α mul) (@AssociativeMul.mk α (@Mul.mk α mul) mul_assoc)
-/
#guard_msgs in set_option pp.explicit true in #print Semigroup._flat_ctor
#guard_msgs in set_option pp.explicit true in #print Semigroup.mk._flat_ctor
/--
info: Test2_1.Semigroup._flat_ctor.{u_1} {α : Type u_1} (mul : ααα)
info: Test2_1.Semigroup.mk._flat_ctor.{u_1} {α : Type u_1} (mul : ααα)
(mul_assoc : ∀ (x y z : α), @Eq α (mul (mul x y) z) (mul x (mul y z))) : Semigroup α
-/
#guard_msgs in set_option pp.explicit true in #check Semigroup._flat_ctor
#guard_msgs in set_option pp.explicit true in #check Semigroup.mk._flat_ctor
end Test2_1
@ -171,10 +171,10 @@ class Add3 (α : Type _) extends Add2 α, Add α where
h (x : α) : x + x = x
/--
info: Test2_2.Add3._flat_ctor.{u_1} {α : Type u_1} (add : ααα)
info: Test2_2.Add3.mk._flat_ctor.{u_1} {α : Type u_1} (add : ααα)
(h : ∀ (x : α), @Eq α (@HAdd.hAdd α α α (@instHAdd α (@Add.mk α add)) x x) x) : Add3 α
-/
#guard_msgs in set_option pp.explicit true in #check Add3._flat_ctor
#guard_msgs in set_option pp.explicit true in #check Add3.mk._flat_ctor
end Test2_2
@ -243,7 +243,7 @@ info: Test4.Semigroup.mk.{u_1} (toMagma : Magma)
#guard_msgs in set_option pp.explicit true in #check Semigroup.mk
/--
info: def Test4.Semigroup._flat_ctor.{u_1} : (α : Type u_1) →
info: def Test4.Semigroup.mk._flat_ctor.{u_1} : (α : Type u_1) →
(mul : ααα) →
(∀ (a b c : α),
@Eq α (@Test4.mul (Magma.mk α mul) (@Test4.mul (Magma.mk α mul) a b) c)
@ -251,7 +251,7 @@ info: def Test4.Semigroup._flat_ctor.{u_1} : (α : Type u_1) →
Semigroup :=
fun α mul mul_assoc => Semigroup.mk (Magma.mk α mul) mul_assoc
-/
#guard_msgs in set_option pp.explicit true in #print Semigroup._flat_ctor
#guard_msgs in set_option pp.explicit true in #print Semigroup.mk._flat_ctor
end Test4
@ -332,10 +332,10 @@ structure A2 extends A1 where
h : n > 0
/--
info: def Test7.A2._flat_ctor : (n : Nat) → n > 0 → A2 :=
info: def Test7.A2.mk._flat_ctor : (n : Nat) → n > 0 → A2 :=
fun n h => A2.mk (A1.mk n) h
-/
#guard_msgs in #print A2._flat_ctor
#guard_msgs in #print A2.mk._flat_ctor
end Test7
@ -384,23 +384,22 @@ structure S4 extends S2, S3
#guard_msgs in #check S3.mk
/-- info: TestO1.S4.mk (toS2 : S2) : S4 -/
#guard_msgs in #check S4.mk
/-- info: TestO1.S1._flat_ctor (x : Nat := by exact 0) : S1 -/
#guard_msgs in #check S1._flat_ctor
/-- info: TestO1.S2._flat_ctor (x : Nat := by exact 0) : S2 -/
#guard_msgs in #check S2._flat_ctor
/-- info: TestO1.S3._flat_ctor (x : Nat := by exact 1) : S3 -/
#guard_msgs in #check S3._flat_ctor
/-- info: TestO1.S4._flat_ctor (x : Nat := by exact 1) : S4 -/
#guard_msgs in #check S4._flat_ctor
/-- info: TestO1.S1.mk._flat_ctor (x : Nat := by exact 0) : S1 -/
#guard_msgs in #check S1.mk._flat_ctor
/-- info: TestO1.S2.mk._flat_ctor (x : Nat := by exact 0) : S2 -/
#guard_msgs in #check S2.mk._flat_ctor
/-- info: TestO1.S3.mk._flat_ctor (x : Nat := by exact 1) : S3 -/
#guard_msgs in #check S3.mk._flat_ctor
/-- info: TestO1.S4.mk._flat_ctor (x : Nat := by exact 1) : S4 -/
#guard_msgs in #check S4.mk._flat_ctor
-- TODO These don't work yet. Need to fix structure instance notation elaborator.
/-- info: S1.mk 0 : S1 -/
#guard_msgs in #check { : S1 }
/-- info: S2.mk (S1.mk 0) : S2 -/
#guard_msgs in #check { : S2 }
/-- info: S3.mk (S1.mk 0) : S3 -/
/-- info: S3.mk (S1.mk 1) : S3 -/
#guard_msgs in #check { : S3 }
/-- info: S4.mk (S2.mk (S1.mk 0)) : S4 -/
/-- info: S4.mk (S2.mk (S1.mk 1)) : S4 -/
#guard_msgs in #check { : S4 }
end TestO1
@ -426,27 +425,26 @@ structure S4 extends S2, S3
#guard_msgs in #check S3.mk
/-- info: TestO2.S4.mk (toS2 : S2) : S4 -/
#guard_msgs in #check S4.mk
/-- info: TestO2.S1._flat_ctor (x : Nat := by exact 0) : S1 -/
#guard_msgs in #check S1._flat_ctor
/-- info: TestO2.S2._flat_ctor (x : Nat := by exact 0) : S2 -/
#guard_msgs in #check S2._flat_ctor
/-- info: TestO2.S3._flat_ctor (x : Nat) : S3 -/
#guard_msgs in #check S3._flat_ctor
/-- info: TestO2.S1.mk._flat_ctor (x : Nat := by exact 0) : S1 -/
#guard_msgs in #check S1.mk._flat_ctor
/-- info: TestO2.S2.mk._flat_ctor (x : Nat := by exact 0) : S2 -/
#guard_msgs in #check S2.mk._flat_ctor
/-- info: TestO2.S3.mk._flat_ctor (x : Nat) : S3 -/
#guard_msgs in #check S3.mk._flat_ctor
/-- info: TestO2.S3.x._default : Nat -/
#guard_msgs in #check S3.x._default
/-- info: TestO2.S4._flat_ctor (x : Nat) : S4 -/
#guard_msgs in #check S4._flat_ctor
/-- info: TestO2.S4.mk._flat_ctor (x : Nat) : S4 -/
#guard_msgs in #check S4.mk._flat_ctor
/-- info: TestO2.S4.x._inherited_default : Nat -/
#guard_msgs in #check S4.x._inherited_default
-- TODO These don't work yet. Need to fix structure instance notation elaborator.
/-- info: S1.mk 0 : S1 -/
#guard_msgs in #check { : S1 }
/-- info: S2.mk (S1.mk 0) : S2 -/
#guard_msgs in #check { : S2 }
/-- info: S3.mk (S1.mk 0) : S3 -/
/-- info: S3.mk (S1.mk 1) : S3 -/
#guard_msgs in #check { : S3 }
/-- info: S4.mk (S2.mk (S1.mk 0)) : S4 -/
/-- info: S4.mk (S2.mk (S1.mk 1)) : S4 -/
#guard_msgs in #check { : S4 }
end TestO2

View file

@ -150,10 +150,21 @@ Responds to recovery mode. In these, `ctac` continues even though configuration
error: structure 'C' does not have a field named 'x'
---
info: config is { b := { toA := { x := true } } }
---
info: ⊢ True
-/
#guard_msgs in
example : True := by
ctac -x
trace_state
trivial
-- Check that when recovery mode is false, no error is reported.
/-- info: ⊢ True -/
#guard_msgs in
example : True := by
fail_if_success ctac -x
trace_state
trivial
/--

View file

@ -1,3 +1,3 @@
structInst1.lean:12:11-12:19: error: field 'toA' has already been specified
structInst1.lean:12:21-12:22: error: field 'x' has already been specified
f5 (c : C) (a : A) : C
f6 (c : C) (a : A) : A

View file

@ -1,9 +0,0 @@
def b : B :=
let __src := a;
{ toA := __src }
def c : C :=
let __src := a;
{ toB := { toA := __src } }
def d : D :=
let __src := c;
{ toB := __src.toB }

View file

@ -1,7 +1,7 @@
def foo : A → B → B :=
fun a b => { x := a.x, y := b.y }
def boo : A → B → B :=
fun a b => { x := b.x, y := b.y }
fun a b => b
def baz : A → B → C :=
fun a b => { toB := { x := a.x, y := b.y } }
def biz : A → B → C :=
@ -9,4 +9,4 @@ fun a b => { toB := b }
def faz : A → C → C :=
fun a c => { toB := { x := a.x, y := c.y } }
def fiz : A → C → C :=
fun a c => { toB := c.toB }
fun a c => c

View file

@ -17,7 +17,7 @@ error: overloaded, errors ⏎
Additional diagnostic information may be available using the `set_option diagnostics true` command.
invalid {...} notation, constructor for `Name` is marked as private
invalid {...} notation, constructor for 'Name' is marked as private
-/
#guard_msgs in
def m1 : Name "hello" := {}