feat: labeled and unique sorries (#5757)

This PR makes it harder to create "fake" theorems about definitions that
are stubbed-out with `sorry` by ensuring that each `sorry` is not
definitionally equal to any other. For example, this now fails:
```lean
example : (sorry : Nat) = sorry := rfl -- fails
```
However, this still succeeds, since the `sorry` is a single
indeterminate `Nat`:
```lean
def f (n : Nat) : Nat := sorry
example : f 0 = f 1 := rfl -- succeeds
```
One can be more careful by putting parameters to the right of the colon:
```lean
def f : (n : Nat) → Nat := sorry
example : f 0 = f 1 := rfl -- fails
```
Most sources of synthetic sorries (recall: a sorry that originates from
the elaborator) are now unique, except for elaboration errors, since
making these unique tends to cause a confusing cascade of errors. In
general, however, such sorries are labeled. This enables "go to
definition" on `sorry` in the Infoview, which brings you to its origin.
The option `set_option pp.sorrySource true` causes the pretty printer to
show source position information on sorries.

**Details:**

* Adds `Lean.Meta.mkLabeledSorry`, which creates a sorry that is labeled
with its source position. For example, `(sorry : Nat)` might elaborate
to
  ```
sorryAx (Lean.Name → Nat) false
`lean.foo.12.8.12.13.8.13._sorry._@.lean.foo._hyg.153
  ```
It can either be made unique (like the above) or merely labeled. Labeled
sorries use an encoding that does not impact defeq:
  ```
sorryAx (Unit → Nat) false (Function.const Lean.Name ()
`lean.foo.14.7.13.7.13.69._sorry._@.lean.foo._hyg.174)
  ```

* Makes the `sorry` term, the `sorry` tactic, and every elaboration
failure create labeled sorries. Most are unique sorries, but some
elaboration errors are labeled sorries.

* Renames `OmissionInfo` to `DelabTermInfo` and adds configuration
options to control LSP interactions. One field is a source position to
use for "go to definition". This is used to implement "go to definition"
on labeled sorries.

* Makes hovering over a labeled `sorry` show something friendlier than
that full `sorryAx` expression. Instead, the first hover shows the
simplified ``sorry `«lean.foo:48:11»``. Hovering over that hover shows
the full `sorryAx`. Setting `set_option pp.sorrySource true` makes
`sorry` always start with printing with this source position
information.

* Removes `Lean.Meta.mkSyntheticSorry` in favor of `Lean.Meta.mkSorry`
and `Lean.Meta.mkLabeledSorry`.

* Changes `sorryAx` so that the `synthetic` argument is no longer
optional.

* Gives `addPPExplicitToExposeDiff` awareness of labeled sorries. It can
set `pp.sorrySource` when source positions differ.

* Modifies the delaborator framework so that delaborators can set Info
themselves without it being overwritten.

Incidentally closes #4972.

Inspired by [this Zulip
thread](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Is.20a.20.60definition_wanted.60.20keyword.20possible.3F/near/477260277).
This commit is contained in:
Kyle Miller 2024-12-11 15:53:02 -08:00 committed by GitHub
parent a64a17e914
commit 58f8e21502
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 464 additions and 137 deletions

View file

@ -168,11 +168,6 @@ end Lean
| `($(_) $c $t $e) => `(if $c then $t else $e)
| _ => throw ()
@[app_unexpander sorryAx] def unexpandSorryAx : Lean.PrettyPrinter.Unexpander
| `($(_) $_) => `(sorry)
| `($(_) $_ $_) => `(sorry)
| _ => throw ()
@[app_unexpander Eq.ndrec] def unexpandEqNDRec : Lean.PrettyPrinter.Unexpander
| `($(_) $m $h) => `($h ▸ $m)
| _ => throw ()

View file

@ -645,23 +645,22 @@ set_option linter.unusedVariables.funArgs false in
@[reducible] def namedPattern {α : Sort u} (x a : α) (h : Eq x a) : α := a
/--
Auxiliary axiom used to implement `sorry`.
Auxiliary axiom used to implement the `sorry` term and tactic.
The `sorry` term/tactic expands to `sorryAx _ (synthetic := false)`. This is a
proof of anything, which is intended for stubbing out incomplete parts of a
proof while still having a syntactically correct proof skeleton. Lean will give
a warning whenever a proof uses `sorry`, so you aren't likely to miss it, but
you can double check if a theorem depends on `sorry` by using
`#print axioms my_thm` and looking for `sorryAx` in the axiom list.
The `sorry` term/tactic expands to `sorryAx _ (synthetic := false)`.
It is intended for stubbing-out incomplete parts of a value or proof while still having a syntactically correct skeleton.
Lean will give a warning whenever a declaration uses `sorry`, so you aren't likely to miss it,
but you can check if a declaration depends on `sorry` either directly or indirectly by looking for `sorryAx` in the output
of the `#print axioms my_thm` command.
The `synthetic` flag is false when written explicitly by the user, but it is
The `synthetic` flag is false when a `sorry` is written explicitly by the user, but it is
set to `true` when a tactic fails to prove a goal, or if there is a type error
in the expression. A synthetic `sorry` acts like a regular one, except that it
suppresses follow-up errors in order to prevent one error from causing a cascade
suppresses follow-up errors in order to prevent an error from causing a cascade
of other errors because the desired term was not constructed.
-/
@[extern "lean_sorry", never_extract]
axiom sorryAx (α : Sort u) (synthetic := false) : α
axiom sorryAx (α : Sort u) (synthetic : Bool) : α
theorem eq_false_of_ne_true : {b : Bool} → Not (Eq b true) → Eq b false
| true, h => False.elim (h rfl)
@ -3932,6 +3931,13 @@ def getId : Syntax → Name
| ident _ _ val _ => val
| _ => Name.anonymous
/-- Retrieve the immediate info from the Syntax node. -/
def getInfo? : Syntax → Option SourceInfo
| atom info .. => some info
| ident info .. => some info
| node info .. => some info
| missing => none
/-- Retrieve the left-most node or leaf's info in the Syntax tree. -/
partial def getHeadInfo? : Syntax → Option SourceInfo
| atom info _ => some info

View file

@ -408,16 +408,18 @@ example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by ac_rfl
syntax (name := acRfl) "ac_rfl" : tactic
/--
The `sorry` tactic closes the goal using `sorryAx`. This is intended for stubbing out incomplete
parts of a proof while still having a syntactically correct proof skeleton. Lean will give
a warning whenever a proof uses `sorry`, so you aren't likely to miss it, but
you can double check if a theorem depends on `sorry` by using
`#print axioms my_thm` and looking for `sorryAx` in the axiom list.
-/
macro "sorry" : tactic => `(tactic| exact @sorryAx _ false)
The `sorry` tactic is a temporary placeholder for an incomplete tactic proof,
closing the main goal using `exact sorry`.
/-- `admit` is a shorthand for `exact sorry`. -/
macro "admit" : tactic => `(tactic| exact @sorryAx _ false)
This is intended for stubbing-out incomplete parts of a proof while still having a syntactically correct proof skeleton.
Lean will give a warning whenever a proof uses `sorry`, so you aren't likely to miss it,
but you can double check if a theorem depends on `sorry` by looking for `sorryAx` in the output
of the `#print axioms my_thm` command, the axiom used by the implementation of `sorry`.
-/
macro "sorry" : tactic => `(tactic| exact sorry)
/-- `admit` is a synonym for `sorry`. -/
macro "admit" : tactic => `(tactic| sorry)
/--
`infer_instance` is an abbreviation for `exact inferInstance`.

View file

@ -0,0 +1,49 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Data.Position
/-!
# Data types for declaration ranges
The environment extension for declaration ranges is in `Lean.DeclarationRange`.
-/
namespace Lean
/-- Store position information for declarations. -/
structure DeclarationRange where
pos : Position
/-- A precomputed UTF-16 `character` field as in `Lean.Lsp.Position`. We need to store this
because LSP clients want us to report the range in terms of UTF-16, but converting a Unicode
codepoint stored in `Lean.Position` to UTF-16 requires loading and mapping the target source
file, which is IO-heavy. -/
charUtf16 : Nat
endPos : Position
/-- See `charUtf16`. -/
endCharUtf16 : Nat
deriving Inhabited, DecidableEq, Repr
instance : ToExpr DeclarationRange where
toExpr r := mkAppN (mkConst ``DeclarationRange.mk) #[toExpr r.pos, toExpr r.charUtf16, toExpr r.endPos, toExpr r.endCharUtf16]
toTypeExpr := mkConst ``DeclarationRange
structure DeclarationRanges where
range : DeclarationRange
selectionRange : DeclarationRange
deriving Inhabited, Repr
instance : ToExpr DeclarationRanges where
toExpr r := mkAppN (mkConst ``DeclarationRanges.mk) #[toExpr r.range, toExpr r.selectionRange]
toTypeExpr := mkConst ``DeclarationRanges
/--
A declaration location is a declaration range along with the name of the module the declaration resides in.
-/
structure DeclarationLocation where
module : Name
range : DeclarationRange
deriving Inhabited, DecidableEq, Repr

View file

@ -4,38 +4,15 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Data.DeclarationRange
import Lean.MonadEnv
import Lean.AuxRecursor
import Lean.ToExpr
/-!
# Environment extension for declaration ranges
-/
namespace Lean
/-- Store position information for declarations. -/
structure DeclarationRange where
pos : Position
/-- A precomputed UTF-16 `character` field as in `Lean.Lsp.Position`. We need to store this
because LSP clients want us to report the range in terms of UTF-16, but converting a Unicode
codepoint stored in `Lean.Position` to UTF-16 requires loading and mapping the target source
file, which is IO-heavy. -/
charUtf16 : Nat
endPos : Position
/-- See `charUtf16`. -/
endCharUtf16 : Nat
deriving Inhabited, DecidableEq, Repr
instance : ToExpr DeclarationRange where
toExpr r := mkAppN (mkConst ``DeclarationRange.mk) #[toExpr r.pos, toExpr r.charUtf16, toExpr r.endPos, toExpr r.endCharUtf16]
toTypeExpr := mkConst ``DeclarationRange
structure DeclarationRanges where
range : DeclarationRange
selectionRange : DeclarationRange
deriving Inhabited, Repr
instance : ToExpr DeclarationRanges where
toExpr r := mkAppN (mkConst ``DeclarationRanges.mk) #[toExpr r.range, toExpr r.selectionRange]
toTypeExpr := mkConst ``DeclarationRanges
builtin_initialize builtinDeclRanges : IO.Ref (NameMap DeclarationRanges) ← IO.mkRef {}
builtin_initialize declRangeExt : MapDeclarationExtension DeclarationRanges ← mkMapDeclarationExtension

View file

@ -212,9 +212,9 @@ private def elabTParserMacroAux (prec lhsPrec e : Term) : TermElabM Syntax := do
| `(dbg_trace $arg:term; $body) => `(dbgTrace (toString $arg) fun _ => $body)
| _ => Macro.throwUnsupported
@[builtin_term_elab «sorry»] def elabSorry : TermElab := fun stx expectedType? => do
let stxNew ← `(@sorryAx _ false) -- Remark: we use `@` to ensure `sorryAx` will not consume auto params
withMacroExpansion stx stxNew <| elabTerm stxNew expectedType?
@[builtin_term_elab «sorry»] def elabSorry : TermElab := fun _ expectedType? => do
let type ← expectedType?.getDM mkFreshTypeMVar
mkLabeledSorry type (synthetic := false) (unique := true)
/-- Return syntax `Prod.mk elems[0] (Prod.mk elems[1] ... (Prod.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
partial def mkPairs (elems : Array Term) : MacroM Term :=

View file

@ -581,7 +581,7 @@ def elabDefaultOrNonempty : TermElab := fun stx expectedType? => do
else
-- It is in the context of an `unsafe` constant. We can use sorry instead.
-- Another option is to make a recursive application since it is unsafe.
mkSorry expectedType false
mkLabeledSorry expectedType false (unique := true)
builtin_initialize
registerTraceClass `Elab.binop

View file

@ -192,8 +192,12 @@ def FVarAliasInfo.format (info : FVarAliasInfo) : Format :=
def FieldRedeclInfo.format (ctx : ContextInfo) (info : FieldRedeclInfo) : Format :=
f!"FieldRedecl @ {formatStxRange ctx info.stx}"
def OmissionInfo.format (ctx : ContextInfo) (info : OmissionInfo) : IO Format := do
return f!"Omission @ {← TermInfo.format ctx info.toTermInfo}\nReason: {info.reason}"
def DelabTermInfo.format (ctx : ContextInfo) (info : DelabTermInfo) : IO Format := do
let loc := if let some loc := info.location? then f!"{loc.module} {loc.range.pos}-{loc.range.endPos}" else "none"
return f!"DelabTermInfo @ {← TermInfo.format ctx info.toTermInfo}\n\
Location: {loc}\n\
Docstring: {repr info.docString?}\n\
Explicit: {info.explicit}"
def ChoiceInfo.format (ctx : ContextInfo) (info : ChoiceInfo) : Format :=
f!"Choice @ {formatElabInfo ctx info.toElabInfo}"
@ -211,7 +215,7 @@ def Info.format (ctx : ContextInfo) : Info → IO Format
| ofCustomInfo i => pure <| Std.ToFormat.format i
| ofFVarAliasInfo i => pure <| i.format
| ofFieldRedeclInfo i => pure <| i.format ctx
| ofOmissionInfo i => i.format ctx
| ofDelabTermInfo i => i.format ctx
| ofChoiceInfo i => pure <| i.format ctx
def Info.toElabInfo? : Info → Option ElabInfo
@ -227,7 +231,7 @@ def Info.toElabInfo? : Info → Option ElabInfo
| ofCustomInfo _ => none
| ofFVarAliasInfo _ => none
| ofFieldRedeclInfo _ => none
| ofOmissionInfo i => some i.toElabInfo
| ofDelabTermInfo i => some i.toElabInfo
| ofChoiceInfo i => some i.toElabInfo
/--

View file

@ -5,7 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Wojciech Nawrocki, Leonardo de Moura, Sebastian Ullrich
-/
prelude
import Lean.Data.Position
import Lean.Data.DeclarationRange
import Lean.Data.OpenDecl
import Lean.MetavarContext
import Lean.Environment
@ -168,14 +168,22 @@ structure FieldRedeclInfo where
stx : Syntax
/--
Denotes information for the term `⋯` that is emitted by the delaborator when omitting a term
due to `pp.deepTerms false` or `pp.proofs false`. Omission needs to be treated differently from regular terms because
Denotes TermInfo with additional configurations to control interactions with delaborated terms.
For example, the omission term `⋯` that is emitted by the delaborator when omitting a term
due to `pp.deepTerms false` or `pp.proofs false` uses this.
Omission needs to be treated differently from regular terms because
it has to be delaborated differently in `Lean.Widget.InteractiveDiagnostics.infoToInteractive`:
Regular terms are delaborated explicitly, whereas omitted terms are simply to be expanded with
regular delaboration settings.
regular delaboration settings. Additionally, omissions come with a reason for omission.
-/
structure OmissionInfo extends TermInfo where
reason : String
structure DelabTermInfo extends TermInfo where
/-- A source position to use for "go to definition", to override the default. -/
location? : Option DeclarationLocation := none
/-- Text to use to override the docstring. -/
docString? : Option String := none
/-- Whether to use explicit mode when pretty printing the term on hover. -/
explicit : Bool := true
/--
Indicates that all overloaded elaborators failed. The subtrees of a `ChoiceInfo` node are the
@ -198,7 +206,7 @@ inductive Info where
| ofCustomInfo (i : CustomInfo)
| ofFVarAliasInfo (i : FVarAliasInfo)
| ofFieldRedeclInfo (i : FieldRedeclInfo)
| ofOmissionInfo (i : OmissionInfo)
| ofDelabTermInfo (i : DelabTermInfo)
| ofChoiceInfo (i : ChoiceInfo)
deriving Inhabited

View file

@ -1007,7 +1007,7 @@ where
values.mapM (instantiateMVarsProfiling ·)
catch ex =>
logException ex
headers.mapM fun header => mkSorry header.type (synthetic := true)
headers.mapM fun header => withRef header.declId <| mkLabeledSorry header.type (synthetic := true) (unique := true)
let headers ← headers.mapM instantiateMVarsAtHeader
let letRecsToLift ← getLetRecsToLift
let letRecsToLift ← letRecsToLift.mapM instantiateMVarsAtLetRecToLift

View file

@ -9,7 +9,6 @@ import Lean.Compiler.NoncomputableAttr
import Lean.Util.CollectLevelParams
import Lean.Util.NumObjs
import Lean.Util.NumApps
import Lean.PrettyPrinter
import Lean.Meta.AbstractNestedProofs
import Lean.Meta.ForEachExpr
import Lean.Meta.Eqns

View file

@ -20,7 +20,7 @@ private def addAndCompilePartial (preDefs : Array PreDefinition) (useSorry := fa
let all := preDefs.toList.map (·.declName)
forallTelescope preDef.type fun xs type => do
let value ← if useSorry then
mkLambdaFVars xs (← mkSorry type (synthetic := true))
mkLambdaFVars xs (← withRef preDef.ref <| mkLabeledSorry type (synthetic := true) (unique := true))
else
let msg := m!"failed to compile 'partial' definition '{preDef.declName}'"
liftM <| mkInhabitantFor msg xs type
@ -115,7 +115,7 @@ private partial def ensureNoUnassignedLevelMVarsAtPreDef (preDef : PreDefinition
private def ensureNoUnassignedMVarsAtPreDef (preDef : PreDefinition) : TermElabM PreDefinition := do
let pendingMVarIds ← getMVarsAtPreDef preDef
if (← logUnassignedUsingErrorInfos pendingMVarIds) then
let preDef := { preDef with value := (← mkSorry preDef.type (synthetic := true)) }
let preDef := { preDef with value := (← withRef preDef.ref <| mkLabeledSorry preDef.type (synthetic := true) (unique := true)) }
if (← getMVarsAtPreDef preDef).isEmpty then
return preDef
else

View file

@ -14,7 +14,7 @@ open Meta
def admitGoal (mvarId : MVarId) : MetaM Unit :=
mvarId.withContext do
let mvarType ← inferType (mkMVar mvarId)
mvarId.assign (← mkSorry mvarType (synthetic := true))
mvarId.assign (← mkLabeledSorry mvarType (synthetic := true) (unique := true))
def goalsToMessageData (goals : List MVarId) : MessageData :=
MessageData.joinSep (goals.map MessageData.ofGoal) m!"\n\n"

View file

@ -70,7 +70,7 @@ def elabExact?Term : TermElab := fun stx expectedType? => do
if let some suggestions ← librarySearch introdGoal then
if suggestions.isEmpty then logError "`exact?%` didn't find any relevant lemmas"
else logError "`exact?%` could not close the goal. Try `by apply` to see partial suggestions."
mkSorry expectedType (synthetic := true)
mkLabeledSorry expectedType (synthetic := true) (unique := true)
else
addTermSuggestion stx (← instantiateMVars goal).headBeta
instantiateMVars goal

View file

@ -1154,7 +1154,7 @@ private def mkSyntheticSorryFor (expectedType? : Option Expr) : TermElabM Expr :
let expectedType ← match expectedType? with
| none => mkFreshTypeMVar
| some expectedType => pure expectedType
mkSyntheticSorry expectedType
mkLabeledSorry expectedType (synthetic := true) (unique := false)
/--
Log the given exception, and create a synthetic sorry for representing the failed
@ -1260,7 +1260,7 @@ The `tacticCode` syntax is the full `by ..` syntax.
-/
def mkTacticMVar (type : Expr) (tacticCode : Syntax) (kind : TacticMVarKind) : TermElabM Expr := do
if ← pure (debug.byAsSorry.get (← getOptions)) <&&> isProp type then
mkSorry type false
withRef tacticCode <| mkLabeledSorry type false (unique := true)
else
let mvar ← mkFreshExprMVar type MetavarKind.syntheticOpaque
let mvarId := mvar.mvarId!
@ -1851,7 +1851,7 @@ def elabTermEnsuringType (stx : Syntax) (expectedType? : Option Expr) (catchExPo
withRef stx <| ensureHasType expectedType? e errorMsgHeader?
catch ex =>
if (← read).errToSorry && ex matches .error .. then
exceptionToSorry ex expectedType?
withRef stx <| exceptionToSorry ex expectedType?
else
throw ex

View file

@ -17,6 +17,7 @@ import Lean.Meta.Instances
import Lean.Meta.AbstractMVars
import Lean.Meta.SynthInstance
import Lean.Meta.AppBuilder
import Lean.Meta.Sorry
import Lean.Meta.Tactic
import Lean.Meta.KAbstract
import Lean.Meta.RecursorInfo

View file

@ -5,7 +5,6 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Structure
import Lean.Util.Recognizers
import Lean.Meta.SynthInstance
import Lean.Meta.Check
import Lean.Meta.DecLevel
@ -513,10 +512,6 @@ def mkSome (type value : Expr) : MetaM Expr := do
let u ← getDecLevel type
return mkApp2 (mkConst ``Option.some [u]) type value
def mkSorry (type : Expr) (synthetic : Bool) : MetaM Expr := do
let u ← getLevel type
return mkApp2 (mkConst ``sorryAx [u]) type (toExpr synthetic)
/-- Return `Decidable.decide p` -/
def mkDecide (p : Expr) : MetaM Expr :=
mkAppOptM ``Decidable.decide #[p, none]
@ -545,10 +540,6 @@ def mkDefault (α : Expr) : MetaM Expr :=
def mkOfNonempty (α : Expr) : MetaM Expr := do
mkAppOptM ``Classical.ofNonempty #[α, none]
/-- Return `sorryAx type` -/
def mkSyntheticSorry (type : Expr) : MetaM Expr :=
return mkApp2 (mkConst ``sorryAx [← getLevel type]) type (mkConst ``Bool.true)
/-- Return `funext h` -/
def mkFunExt (h : Expr) : MetaM Expr :=
mkAppM ``funext #[h]

View file

@ -5,6 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Meta.InferType
import Lean.Meta.Sorry
/-!
This is not the Kernel type checker, but an auxiliary method for checking
@ -133,6 +134,12 @@ where
if fn? == ``OfNat.ofNat && as.size ≥ 3 && firstImplicitDiff? == some 0 then
-- Even if there is an explicit diff, it is better to see that the type is different.
return (a.setPPNumericTypes true, b.setPPNumericTypes true)
if fn? == ``sorryAx then
-- If these are `sorry`s with differing source positions, make sure the delaborator shows the positions.
if let some { module? := moda? } := isLabeledSorry? a then
if let some { module? := modb? } := isLabeledSorry? b then
if moda? != modb? then
return (a.setOption `pp.sorrySource true, b.setOption `pp.sorrySource true)
-- General case
if let some i := firstExplicitDiff? <|> firstImplicitDiff? then
let (ai, bi) ← visit as[i]! bs[i]!

115
src/Lean/Meta/Sorry.lean Normal file
View file

@ -0,0 +1,115 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kyle Miller
-/
prelude
import Lean.Data.Lsp.Utf16
import Lean.Meta.InferType
import Lean.Util.Recognizers
/-!
# Utilities for creating and recognizing `sorry`
This module develops material for creating and recognizing `sorryAx` terms that encode originating source positions.
There are three orthogonal configurations for sorries:
- The sorry could be *synthetic*. When elaboration fails on some subterm, then we can use a sorry to fill in the missing subterm.
In this case elaboration marks the sorry as being "synthetic" while logging an error.
The presence of synthetic sorries tends to suppress further errors. For example, the "this declaration contains sorry" error
is not triggered for synthetic sorries as we assume there is already an error message logged.
- The sorry could be *unique*. A unique sorry is not definitionally equal to any other sorry, even if they have the same type.
Normally `sorryAx α s = sorryAx α s` is a definitional equality. Unique sorries insert a unique tag `t` using the encoding `sorryAx (τ → α) s t`.
- The sorry could be *labeled*. A labeled sorry contains source position information, supporting the LSP "go to definition" feature in the Infoview,
and also supporting pretty printing the sorry with an indication of source position when the option `pp.sorrySource` is true.
-/
namespace Lean.Meta
/--
Returns `sorryAx type synthetic`. Recall that `synthetic` is true if this sorry is from an error.
See also `Lean.Meta.mkLabeledSorry`, for creating a `sorry` that is labeled or unique.
-/
def mkSorry (type : Expr) (synthetic : Bool) : MetaM Expr := do
let u ← getLevel type
return mkApp2 (mkConst ``sorryAx [u]) type (toExpr synthetic)
structure SorryLabelView where
/--
Records the origin module name, logical source position, and LSP range for the `sorry`.
The logical source position is used when displaying the sorry when the `pp.sorrySource` option is true,
and the LSP range is used for "go to definition" in the Infoview.
-/
module? : Option DeclarationLocation := none
def SorryLabelView.encode (view : SorryLabelView) : CoreM Name :=
let name :=
if let some { module, range := { pos, endPos, charUtf16, endCharUtf16 } } := view.module? then
module
|>.num pos.line |>.num pos.column
|>.num endPos.line |>.num endPos.column
|>.num charUtf16 |>.num endCharUtf16
else
.anonymous
mkFreshUserName (name.str "_sorry")
def SorryLabelView.decode? (name : Name) : Option SorryLabelView := do
guard <| name.hasMacroScopes
let .str name "_sorry" := name.eraseMacroScopes | failure
if let .num (.num (.num (.num (.num (.num module posLine) posCol) endLine) endCol) charUtf16) endCharUtf16 := name then
return { module? := some { module, range := { pos := ⟨posLine, posCol⟩, endPos := ⟨endLine, endCol⟩, charUtf16, endCharUtf16 } } }
else
failure
/--
Constructs a `sorryAx`.
* If the current ref has a source position, then creates a labeled sorry.
This supports "go to definition" in the InfoView and pretty printing a source position when the `pp.sorrySource` option is true.
* If `synthetic` is true, then the `sorry` is regarded as being generated by the elaborator.
The caller should ensure that there is an associated error logged.
* If `unique` is true, the `sorry` is unique, in the sense that it is not defeq to any other `sorry` created by `mkLabeledSorry`.
-/
def mkLabeledSorry (type : Expr) (synthetic : Bool) (unique : Bool) : MetaM Expr := do
let tag ←
if let (some startSPos, some endSPos) := ((← getRef).getPos?, (← getRef).getTailPos?) then
let fileMap ← getFileMap
SorryLabelView.encode {
module? := some {
module := (← getMainModule)
range := {
pos := fileMap.toPosition startSPos
endPos := fileMap.toPosition endSPos
charUtf16 := (fileMap.utf8PosToLspPos startSPos).character
endCharUtf16 := (fileMap.utf8PosToLspPos endSPos).character
}
}
}
else
SorryLabelView.encode {}
if unique then
let e ← mkSorry (mkForall `tag .default (mkConst ``Lean.Name) type) synthetic
return .app e (toExpr tag)
else
let e ← mkSorry (mkForall `tag .default (mkConst ``Unit) type) synthetic
let tag' := mkApp4 (mkConst ``Function.const [levelOne, levelOne]) (mkConst ``Unit) (mkConst ``Lean.Name) (mkConst ``Unit.unit) (toExpr tag)
return .app e tag'
/--
Returns a `SorryLabelView` if `e` is an application of an expression returned by `mkLabeledSorry`.
If it is, then the `sorry` takes the first three arguments, and the tag is at argument 3.
-/
def isLabeledSorry? (e : Expr) : Option SorryLabelView := do
guard <| e.isAppOf ``sorryAx
let numArgs := e.getAppNumArgs
guard <| numArgs ≥ 3
let arg := e.getArg! 2
if let some tag := arg.name? then
SorryLabelView.decode? tag
else
guard <| arg.isAppOfArity ``Function.const 4
guard <| arg.appFn!.appArg!.isAppOfArity ``Unit.unit 0
let tag ← arg.appArg!.name?
SorryLabelView.decode? tag

View file

@ -60,7 +60,7 @@ def _root_.Lean.MVarId.admit (mvarId : MVarId) (synthetic := true) : MetaM Unit
mvarId.withContext do
mvarId.checkNotAssigned `admit
let mvarType ← mvarId.getType
let val ← mkSorry mvarType synthetic
let val ← mkLabeledSorry mvarType synthetic (unique := true)
mvarId.assign val
/-- Beta reduce the metavariable type head -/

View file

@ -215,7 +215,23 @@ However, in case it is copied and pasted from the Infoview, `⋯` logs a warning
@[builtin_term_parser] def omission := leading_parser
"⋯"
def binderIdent : Parser := ident <|> hole
/-- A temporary placeholder for a missing proof or value. -/
/--
The `sorry` term is a temporary placeholder for a missing proof or value.
The syntax is intended for stubbing-out incomplete parts of a value or proof while still having a syntactically correct skeleton.
Lean will give a warning whenever a declaration uses `sorry`, so you aren't likely to miss it,
but you can double check if a declaration depends on `sorry` by looking for `sorryAx` in the output
of the `#print axioms my_thm` command, the axiom used by the implementation of `sorry`.
"Go to definition" on `sorry` in the Infoview will go to the source position where it was introduced, if such information is available.
Each `sorry` is guaranteed to be unique, so for example the following fails:
```lean
example : (sorry : Nat) = sorry := rfl -- fails
```
See also the `sorry` tactic, which is short for `exact sorry`.
-/
@[builtin_term_parser] def «sorry» := leading_parser
"sorry"
/--

View file

@ -211,6 +211,16 @@ where
stx := stx
}
def addDelabTermInfo (pos : Pos) (stx : Syntax) (e : Expr) (isBinder : Bool := false)
(location? : Option DeclarationLocation := none) (docString? : Option String := none) (explicit : Bool := true) : DelabM Unit := do
let info := Info.ofDelabTermInfo {
toTermInfo := ← addTermInfo.mkTermInfo stx e (isBinder := isBinder)
location? := location?
docString? := docString?
explicit := explicit
}
modify fun s => { s with infos := s.infos.insert pos info }
/--
Annotates the term with the current expression position and registers `TermInfo`
to associate the term to the current expression.
@ -221,13 +231,30 @@ def annotateTermInfo (stx : Term) : Delab := do
pure stx
/--
Modifies the delaborator so that it annotates the resulting term with the current expression
position and registers `TermInfo` to associate the term to the current expression.
Annotates the term with the current expression position and registers `TermInfo`
to associate the term to the current expression, unless the syntax has a synthetic position
and associated `Info` already.
-/
def annotateTermInfoUnlessAnnotated (stx : Term) : Delab := do
if let some (.synthetic ⟨pos⟩ ⟨pos'⟩) := stx.raw.getInfo? then
if pos == pos' && (← get).infos.contains pos then
return stx
annotateTermInfo stx
/--
Modifies the delaborator so that it annotates the resulting term using `annotateTermInfo`.
-/
def withAnnotateTermInfo (d : Delab) : Delab := do
let stx ← d
annotateTermInfo stx
/--
Modifies the delaborator so that it ensures resulting term is annotated using `annotateTermInfoUnlessAnnotated`.
-/
def withAnnotateTermInfoUnlessAnnotated (d : Delab) : Delab := do
let stx ← d
annotateTermInfoUnlessAnnotated stx
/--
Gets an name based on `suggestion` that is unused in the local context.
Erases macro scopes.
@ -294,13 +321,7 @@ def OmissionReason.toString : OmissionReason → String
| maxSteps => "Term omitted due to reaching the maximum number of steps allowed for pretty printing this expression (see option `pp.maxSteps`)."
def addOmissionInfo (pos : Pos) (stx : Syntax) (e : Expr) (reason : OmissionReason) : DelabM Unit := do
let info := Info.ofOmissionInfo <| ← mkOmissionInfo stx e
modify fun s => { s with infos := s.infos.insert pos info }
where
mkOmissionInfo stx e := return {
toTermInfo := ← addTermInfo.mkTermInfo stx e (isBinder := false)
reason := reason.toString
}
addDelabTermInfo pos stx e (docString? := reason.toString) (explicit := false)
/--
Runs the delaborator `act` with increased depth.
@ -381,7 +402,7 @@ def omission (reason : OmissionReason) : Delab := do
partial def delabFor : Name → Delab
| Name.anonymous => failure
| k =>
(do annotateTermInfo (← (delabAttribute.getValues (← getEnv) k).firstM id))
(do annotateTermInfoUnlessAnnotated (← (delabAttribute.getValues (← getEnv) k).firstM id))
-- have `app.Option.some` fall back to `app` etc.
<|> if k.isAtomic then failure else delabFor k.getRoot
@ -433,7 +454,7 @@ open SubExpr (Pos PosMap)
open Delaborator (OptionsPerPos topDownAnalyze DelabM)
def delabCore (e : Expr) (optionsPerPos : OptionsPerPos := {}) (delab : DelabM α) :
MetaM (α × PosMap Elab.Info) := do
MetaM (α × PosMap Elab.Info) := do
/- Using `erasePatternAnnotations` here is a bit hackish, but we do it
`Expr.mdata` affects the delaborator. TODO: should we fix that? -/
let e ← Meta.erasePatternRefAnnotations e
@ -453,7 +474,8 @@ def delabCore (e : Expr) (optionsPerPos : OptionsPerPos := {}) (delab : DelabM
topDownAnalyze e
else pure optionsPerPos
let (stx, {infos := infos, ..}) ← catchInternalId Delaborator.delabFailureId
(delab
-- Clear the ref to ensure that quotations in delaborators start with blank source info.
(MonadRef.withRef .missing delab
{ optionsPerPos := optionsPerPos
currNamespace := (← getCurrNamespace)
openDecls := (← getOpenDecls)

View file

@ -587,7 +587,7 @@ def withOverApp (arity : Nat) (x : Delab) : Delab := do
else
let delabHead (insertExplicit : Bool) : Delab := do
guard <| !insertExplicit
withAnnotateTermInfo x
withAnnotateTermInfoUnlessAnnotated x
delabAppCore (n - arity) delabHead (unexpand := false)
@[builtin_delab app]
@ -1287,6 +1287,35 @@ def delabNameMkStr : Delab := whenPPOption getPPNotation do
@[builtin_delab app.Lean.Name.num]
def delabNameMkNum : Delab := delabNameMkStr
@[builtin_delab app.sorryAx]
def delabSorry : Delab := whenPPOption getPPNotation <| whenNotPPOption getPPExplicit do
let numArgs := (← getExpr).getAppNumArgs
guard <| numArgs ≥ 2
-- Cache the current position's value, since it might be applied to the entire application.
let sorrySource ← getPPOption getPPSorrySource
-- If this is constructed by `Lean.Meta.mkLabeledSorry`, then normally we don't print the unique tag.
-- But, if `pp.explicit` is false and `pp.sorrySource` is true, then print a simplified version of the tag.
if let some view := isLabeledSorry? (← getExpr) then
withOverApp 3 do
if let some loc := view.module? then
let (stx, explicit) ←
if ← pure sorrySource <||> getPPOption getPPSorrySource then
let posAsName := Name.mkSimple s!"{loc.module}:{loc.range.pos.line}:{loc.range.pos.column}"
let pos := mkNode ``Lean.Parser.Term.quotedName #[Syntax.mkNameLit s!"`{posAsName}"]
let src ← withAppArg <| annotateTermInfo pos
pure (← `(sorry $src), true)
else
-- Set `explicit` to false so that the first hover sets `pp.sorrySource` to true in `Lean.Widget.ppExprTagged`
pure (← `(sorry), false)
let stx ← annotateCurPos stx
addDelabTermInfo (← getPos) stx (← getExpr) (explicit := explicit) (location? := loc)
(docString? := "This is a `sorry` term associated to a source position. Use 'Go to definition' to go there.")
return stx
else
`(sorry)
else
withOverApp 2 `(sorry)
open Parser Command Term in
@[run_builtin_parser_attribute_hooks]
-- use `termParser` instead of `declId` so we can reuse `delabConst`

View file

@ -39,6 +39,11 @@ register_builtin_option pp.match : Bool := {
group := "pp"
descr := "(pretty printer) disable/enable 'match' notation"
}
register_builtin_option pp.sorrySource : Bool := {
defValue := false
group := "pp"
descr := "(pretty printer) if true, pretty print 'sorry' with its originating source position, if available"
}
register_builtin_option pp.coercions : Bool := {
defValue := true
group := "pp"
@ -262,6 +267,7 @@ def getPPNotation (o : Options) : Bool := o.get pp.notation.name (!getPPAll o)
def getPPParens (o : Options) : Bool := o.get pp.parens.name pp.parens.defValue
def getPPUnicodeFun (o : Options) : Bool := o.get pp.unicode.fun.name false
def getPPMatch (o : Options) : Bool := o.get pp.match.name (!getPPAll o)
def getPPSorrySource (o : Options) : Bool := o.get pp.sorrySource.name pp.sorrySource.defValue
def getPPFieldNotation (o : Options) : Bool := o.get pp.fieldNotation.name (!getPPAll o)
def getPPFieldNotationGeneralized (o : Options) : Bool := o.get pp.fieldNotation.generalized.name pp.fieldNotation.generalized.defValue
def getPPStructureInstances (o : Options) : Bool := o.get pp.structureInstances.name (!getPPAll o)

View file

@ -152,13 +152,22 @@ def locationLinksOfInfo (kind : GoToKind) (ictx : InfoWithCtx)
let i := ictx.info
let ci := ictx.ctx
let children := ictx.children
match i with
| .ofTermInfo ti =>
let locationLinksDefault : RequestM (Array LocationLink) := do
-- If other go-tos fail, we try to show the elaborator or parser
if let some ei := i.toElabInfo? then
if kind == declaration && ci.env.contains ei.stx.getKind then
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.stx.getKind
if kind == definition && ci.env.contains ei.elaborator then
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.elaborator
return #[]
let locationLinksFromTermInfo (ti : TermInfo) : RequestM (Array LocationLink) := do
let mut expr := ti.expr
if kind == type then
expr ← ci.runMetaM i.lctx do
return Expr.getAppFn (← instantiateMVars (← Meta.inferType expr))
match expr with
match expr.consumeMData with
| Expr.const n .. => return ← ci.runMetaM i.lctx <| locationLinksFromDecl i n
| Expr.fvar id .. => return ← ci.runMetaM i.lctx <| locationLinksFromBinder i id
| _ => pure ()
@ -176,7 +185,7 @@ def locationLinksOfInfo (kind : GoToKind) (ictx : InfoWithCtx)
-- go-to-definition on a projection application of a typeclass
-- should return all instances generated by TC
expr ← ci.runMetaM i.lctx do instantiateMVars expr
if let .const n _ := expr.getAppFn then
if let .const n _ := expr.getAppFn.consumeMData then
-- also include constant along with instance results
let mut results ← ci.runMetaM i.lctx <| locationLinksFromDecl i n
if let some info := ci.env.getProjectionFnInfo? n then
@ -193,12 +202,29 @@ def locationLinksOfInfo (kind : GoToKind) (ictx : InfoWithCtx)
| .const declName _ => do
if ← ci.runMetaM i.lctx do Lean.Meta.isInstance declName then pure #[declName] else pure #[]
| .app fn arg => do pure $ (← extractInstances fn).append (← extractInstances arg)
| .mdata _ e => extractInstances e
| _ => pure #[]
if let some instArg := appArgs.get? instIdx then
for inst in (← extractInstances instArg) do
results := results.append (← ci.runMetaM i.lctx <| locationLinksFromDecl i inst)
results := results.append elaborators -- put elaborators at the end of the results
return results
locationLinksDefault
match i with
| .ofTermInfo ti =>
return ← locationLinksFromTermInfo ti
| .ofDelabTermInfo { toTermInfo := ti, location?, .. } =>
if let some location := location? then
if let some targetUri ← documentUriFromModule rc.srcSearchPath location.module then
let range := location.range.toLspRange
let result : LocationLink := {
targetUri, targetRange := range, targetSelectionRange := range,
originSelectionRange? := (·.toLspRange text) <$> i.range?
}
return #[result]
-- If we fail to find a DocumentUri, fall through and use the default method to at least try to have something to jump to.
return ← locationLinksFromTermInfo ti
| .ofFieldInfo fi =>
if kind == type then
let expr ← ci.runMetaM i.lctx do
@ -213,13 +239,7 @@ def locationLinksOfInfo (kind : GoToKind) (ictx : InfoWithCtx)
if kind == definition || kind == declaration then
return ← ci.runMetaM i.lctx <| locationLinksFromImport i
| _ => pure ()
-- If other go-tos fail, we try to show the elaborator or parser
if let some ei := i.toElabInfo? then
if kind == declaration && ci.env.contains ei.stx.getKind then
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.stx.getKind
if kind == definition && ci.env.contains ei.elaborator then
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.elaborator
return #[]
locationLinksDefault
open Elab GoToKind in
def handleDefinition (kind : GoToKind) (p : TextDocumentPositionParams)

View file

@ -53,13 +53,12 @@ def makePopup : WithRpcRef InfoWithCtx → RequestM (RequestTask InfoPopup)
| some type => some <$> ppExprTagged type
| none => pure none
let exprExplicit? ← match i.info with
| Elab.Info.ofTermInfo ti =>
pure <| some <| ← ppExprTaggedWithoutTopLevelHighlight ti.expr (explicit := true)
| Elab.Info.ofOmissionInfo { toTermInfo := ti, .. } =>
-- Omitted terms are simply to be expanded, not printed explicitly.
-- Keep the top-level tag so that users can also see the explicit version
-- of the omitted term.
pure <| some <| ← ppExprTagged ti.expr (explicit := false)
| Elab.Info.ofTermInfo ti
| Elab.Info.ofDelabTermInfo { toTermInfo := ti, explicit := true, ..} =>
some <$> ppExprTaggedWithoutTopLevelHighlight ti.expr (explicit := true)
| Elab.Info.ofDelabTermInfo { toTermInfo := ti, explicit := false, ..} =>
-- Keep the top-level tag so that users can also see the explicit version of the term on an additional hover.
some <$> ppExprTagged ti.expr (explicit := false)
| Elab.Info.ofFieldInfo fi => pure <| some <| TaggedText.text fi.fieldName.toString
| _ => pure none
return {

View file

@ -146,13 +146,13 @@ def Info.stx : Info → Syntax
| ofUserWidgetInfo i => i.stx
| ofFVarAliasInfo _ => .missing
| ofFieldRedeclInfo i => i.stx
| ofOmissionInfo i => i.stx
| ofDelabTermInfo i => i.stx
| ofChoiceInfo i => i.stx
def Info.lctx : Info → LocalContext
| .ofTermInfo i => i.lctx
| .ofFieldInfo i => i.lctx
| .ofOmissionInfo i => i.lctx
| .ofDelabTermInfo i => i.lctx
| .ofMacroExpansionInfo i => i.lctx
| .ofCompletionInfo i => i.lctx
| _ => LocalContext.empty
@ -251,15 +251,17 @@ partial def InfoTree.hoverableInfoAt? (t : InfoTree) (hoverPos : String.Pos) (in
def Info.type? (i : Info) : MetaM (Option Expr) :=
match i with
| Info.ofTermInfo ti => Meta.inferType ti.expr
| Info.ofFieldInfo fi => Meta.inferType fi.val
| Info.ofOmissionInfo oi => Meta.inferType oi.expr
| Info.ofTermInfo ti => Meta.inferType ti.expr
| Info.ofFieldInfo fi => Meta.inferType fi.val
| Info.ofDelabTermInfo ti => Meta.inferType ti.expr
| _ => return none
def Info.docString? (i : Info) : MetaM (Option String) := do
let env ← getEnv
match i with
| .ofTermInfo ti =>
| .ofDelabTermInfo { docString? := some s, .. } => return s
| .ofTermInfo ti
| .ofDelabTermInfo ti =>
if let some n := ti.expr.constName? then
return (← findDocString? env n)
| .ofFieldInfo fi => return ← findDocString? env fi.projName
@ -269,7 +271,6 @@ def Info.docString? (i : Info) : MetaM (Option String) := do
if let some decl := (← getOptionDecls).find? oi.optionName then
return decl.descr
return none
| .ofOmissionInfo { reason := s, .. } => return s -- Show the omission reason for the docstring.
| _ => pure ()
if let some ei := i.toElabInfo? then
return ← findDocString? env ei.stx.getKind <||> findDocString? env ei.elaborator
@ -418,7 +419,7 @@ where go ci?
| .node i cs =>
match ci?, i with
| some ci, .ofTermInfo ti
| some ci, .ofOmissionInfo { toTermInfo := ti, .. } => do
| some ci, .ofDelabTermInfo ti => do
-- NOTE: `instantiateMVars` can potentially be expensive but we rely on the elaborator
-- creating a fully instantiated `MutualDef.body` term info node which has the implicit effect
-- of making the `instantiateMVars` here a no-op and avoids further recursing into the body

View file

@ -9,17 +9,17 @@ import Lean.Declaration
namespace Lean
def Expr.isSorry : Expr → Bool
| app (app (.const ``sorryAx ..) ..) .. => true
| _ => false
/-- Returns `true` if the expression is an application of `sorryAx`. -/
def Expr.isSorry (e : Expr) : Bool :=
e.isAppOf ``sorryAx
def Expr.isSyntheticSorry : Expr → Bool
| app (app (const ``sorryAx ..) ..) (const ``Bool.true ..) => true
| _ => false
/-- Returns `true` if the expression is of the form `sorryAx _ true ..`. -/
def Expr.isSyntheticSorry (e : Expr) : Bool :=
e.isAppOf ``sorryAx && e.getAppNumArgs ≥ 2 && (e.getArg! 1).isConstOf ``Bool.true
def Expr.isNonSyntheticSorry : Expr → Bool
| app (app (const ``sorryAx ..) ..) (const ``Bool.false ..) => true
| _ => false
/-- Returns `true` if the expression is of the form `sorryAx _ false ..`. -/
def Expr.isNonSyntheticSorry (e : Expr) : Bool :=
e.isAppOf ``sorryAx && e.getAppNumArgs ≥ 2 && (e.getArg! 1).isConstOf ``Bool.false
def Expr.hasSorry (e : Expr) : Bool :=
Option.isSome <| e.find? (·.isConstOf ``sorryAx)

View file

@ -84,6 +84,7 @@ def ppExprTagged (e : Expr) (explicit : Bool := false) : MetaM CodeWithInfos :=
delabApp
else
withOptionAtCurrPos pp.proofs.name true do
withOptionAtCurrPos pp.sorrySource.name true do
delab
let mut e := e
-- When hovering over a metavariable, we want to see its value, even if `pp.instantiateMVars` is false.

View file

@ -9,7 +9,6 @@ import Lake.Config.Package
import Lake.CLI.Translate.Toml
import Lake.CLI.Translate.Lean
import Lake.Load.Lean.Elab
import Lean.PrettyPrinter
namespace Lake
open Toml Lean System PrettyPrinter

View file

@ -2,6 +2,8 @@
# Tests of the `sorry` term elaborator
-/
set_option pp.mvars false
/-!
Basic usage.
-/
@ -24,3 +26,81 @@ Pretty printing
/-- info: fun x => sorry (x + 1) : Nat → Nat -/
#guard_msgs in #check fun x : Nat => (sorry : Nat → Nat) (x + 1)
/-!
Uniqueness
-/
/-- warning: declaration uses 'sorry' -/
#guard_msgs in
example : (sorry : Nat) = sorry := by
fail_if_success rfl
sorry
/-- warning: declaration uses 'sorry' -/
#guard_msgs in
def f (n : Nat) : Nat → Nat := sorry
example : f 0 0 = f 0 0 := rfl -- succeeds
/-!
If `sorry` is used for a function type, then one gets a family of unique `sorry`s.
-/
/--
error: type mismatch
rfl
has type
?_ = ?_ : Prop
but is expected to have type
f 0 1 = f 0 0 : Prop
-/
#guard_msgs in example : f 0 1 = f 0 0 := rfl
/-!
It is not completely unique though. The `sorry` did not pay attention to variables in the local context.
-/
#guard_msgs in example : f 1 0 = f 0 0 := rfl
/-!
Showing source position when surfacing differences.
-/
-- note: the module name is `sorry` and not `lean.run.sorry` in the testing environment,
-- so this test fails in VS Code.
/--
error: type mismatch
sorry
has type
sorry `«sorry:77:43» : Sort _
but is expected to have type
sorry `«sorry:77:25» : Sort _
-/
#guard_msgs in example : sorry := (sorry : sorry)
/-!
Elaboration errors are just labeled, not unique, to limit cascading errors.
-/
/--
error: unknown identifier 'a'
---
error: unknown identifier 'b'
---
info: ⊢ sorry = sorry
-/
#guard_msgs in
set_option autoImplicit false in
example : a = b := by trace_state; rfl
/-!
Showing that the sorries in the previous test are labeled.
-/
/--
error: unknown identifier 'a'
---
error: unknown identifier 'b'
---
info: ⊢ sorry `«sorry:106:10» = sorry `«sorry:106:14»
-/
#guard_msgs in
set_option autoImplicit false in
set_option pp.sorrySource true in
example : a = b := by trace_state; rfl

View file

@ -19,7 +19,7 @@ inst✝ inst : α
shadow.lean:17:0-17:1: error: don't know how to synthesize placeholder
context:
α : Type u_1
inst.75 : Inhabited α
inst.78 : Inhabited α
inst inst : α
⊢ {β δ : Type} → α → β → δ → α × β × δ
shadow.lean:20:0-20:1: error: don't know how to synthesize placeholder