Skip to content

Commit a58b494

Browse files
committed
pervasive mkUniqueSorry
1 parent 1efdc9c commit a58b494

File tree

16 files changed

+93
-42
lines changed

16 files changed

+93
-42
lines changed

src/Init/Prelude.lean

+3-5
Original file line numberDiff line numberDiff line change
@@ -648,13 +648,11 @@ set_option linter.unusedVariables.funArgs false in
648648
Auxiliary axiom used to implement the `sorry` term and tactic.
649649
650650
The `sorry` term/tactic expands to `sorryAx _ (synthetic := false)`.
651-
This is intended for stubbing out incomplete parts of a proof while still having a syntactically correct proof skeleton.
652-
Lean will give a warning whenever a proof uses `sorry`, so you aren't likely to miss it,
653-
but you can double check if a theorem depends on `sorry` by looking for `sorryAx` in the output
651+
It intended for stubbing-out incomplete parts of a value or proof while still having a syntactically correct skeleton.
652+
Lean will give a warning whenever a declaration uses `sorry`, so you aren't likely to miss it,
653+
but you can double check if a declaration depends on `sorry` by looking for `sorryAx` in the output
654654
of the `#print axioms my_thm` command.
655655
656-
"Go to definition" on `sorry` will go to the source position where it was introduced.
657-
658656
The `synthetic` flag is false when written explicitly by the user, but it is
659657
set to `true` when a tactic fails to prove a goal, or if there is a type error
660658
in the expression. A synthetic `sorry` acts like a regular one, except that it

src/Init/Tactics.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ syntax (name := acRfl) "ac_rfl" : tactic
403403
The `sorry` tactic is a temporary placeholder for an incomplete tactic proof,
404404
closing the main goal using `exact sorry`.
405405
406-
This is intended for stubbing out incomplete parts of a proof while still having a syntactically correct proof skeleton.
406+
This is intended for stubbing-out incomplete parts of a proof while still having a syntactically correct proof skeleton.
407407
Lean will give a warning whenever a proof uses `sorry`, so you aren't likely to miss it,
408408
but you can double check if a theorem depends on `sorry` by looking for `sorryAx` in the output
409409
of the `#print axioms my_thm` command, which is the axiom used by implementation of `sorry`.

src/Lean/Elab/Extra.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def elabDefaultOrNonempty : TermElab := fun stx expectedType? => do
578578
else
579579
-- It is in the context of an `unsafe` constant. We can use sorry instead.
580580
-- Another option is to make a recursive application since it is unsafe.
581-
mkSorry expectedType false
581+
mkUniqueSorry expectedType false
582582

583583
builtin_initialize
584584
registerTraceClass `Elab.binop

src/Lean/Elab/MutualDef.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ where
977977
values.mapM (instantiateMVarsProfiling ·)
978978
catch ex =>
979979
logException ex
980-
headers.mapM fun header => mkSorry header.type (synthetic := true)
980+
headers.mapM fun header => withRef header.declId <| mkUniqueSorry header.type (synthetic := true)
981981
let headers ← headers.mapM instantiateMVarsAtHeader
982982
let letRecsToLift ← getLetRecsToLift
983983
let letRecsToLift ← letRecsToLift.mapM instantiateMVarsAtLetRecToLift

src/Lean/Elab/PreDefinition/Main.lean

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ private def addAndCompilePartial (preDefs : Array PreDefinition) (useSorry := fa
2020
let all := preDefs.toList.map (·.declName)
2121
forallTelescope preDef.type fun xs type => do
2222
let value ← if useSorry then
23-
mkLambdaFVars xs (← mkSorry type (synthetic := true))
23+
mkLambdaFVars xs (← withRef preDef.ref <| mkUniqueSorry type (synthetic := true))
2424
else
2525
liftM <| mkInhabitantFor preDef.declName xs type
2626
addNonRec { preDef with
@@ -114,7 +114,7 @@ private partial def ensureNoUnassignedLevelMVarsAtPreDef (preDef : PreDefinition
114114
private def ensureNoUnassignedMVarsAtPreDef (preDef : PreDefinition) : TermElabM PreDefinition := do
115115
let pendingMVarIds ← getMVarsAtPreDef preDef
116116
if (← logUnassignedUsingErrorInfos pendingMVarIds) then
117-
let preDef := { preDef with value := (← mkSorry preDef.type (synthetic := true)) }
117+
let preDef := { preDef with value := (← withRef preDef.ref <| mkUniqueSorry preDef.type (synthetic := true)) }
118118
if (← getMVarsAtPreDef preDef).isEmpty then
119119
return preDef
120120
else

src/Lean/Elab/Tactic/Basic.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ open Meta
1414
def admitGoal (mvarId : MVarId) : MetaM Unit :=
1515
mvarId.withContext do
1616
let mvarType ← inferType (mkMVar mvarId)
17-
mvarId.assign (← mkSorry mvarType (synthetic := true))
17+
mvarId.assign (← mkUniqueSorry mvarType (synthetic := true))
1818

1919
def goalsToMessageData (goals : List MVarId) : MessageData :=
2020
MessageData.joinSep (goals.map MessageData.ofGoal) m!"\n\n"

src/Lean/Elab/Tactic/LibrarySearch.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def elabExact?Term : TermElab := fun stx expectedType? => do
7070
if let some suggestions ← librarySearch introdGoal then
7171
if suggestions.isEmpty then logError "`exact?%` didn't find any relevant lemmas"
7272
else logError "`exact?%` could not close the goal. Try `by apply` to see partial suggestions."
73-
mkSorry expectedType (synthetic := true)
73+
mkUniqueSorry expectedType (synthetic := true)
7474
else
7575
addTermSuggestion stx (← instantiateMVars goal).headBeta
7676
instantiateMVars goal

src/Lean/Elab/Term.lean

+3-3
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@ private def mkSyntheticSorryFor (expectedType? : Option Expr) : TermElabM Expr :
11071107
let expectedType ← match expectedType? with
11081108
| none => mkFreshTypeMVar
11091109
| some expectedType => pure expectedType
1110-
mkSorry expectedType (synthetic := true)
1110+
mkUniqueSorry expectedType (synthetic := true)
11111111

11121112
/--
11131113
Log the given exception, and create a synthetic sorry for representing the failed
@@ -1213,7 +1213,7 @@ The `tacticCode` syntax is the full `by ..` syntax.
12131213
-/
12141214
def mkTacticMVar (type : Expr) (tacticCode : Syntax) (kind : TacticMVarKind) : TermElabM Expr := do
12151215
if ← pure (debug.byAsSorry.get (← getOptions)) <&&> isProp type then
1216-
mkSorry type false
1216+
withRef tacticCode <| mkUniqueSorry type false
12171217
else
12181218
let mvar ← mkFreshExprMVar type MetavarKind.syntheticOpaque
12191219
let mvarId := mvar.mvarId!
@@ -1769,7 +1769,7 @@ def elabTermEnsuringType (stx : Syntax) (expectedType? : Option Expr) (catchExPo
17691769
withRef stx <| ensureHasType expectedType? e errorMsgHeader?
17701770
catch ex =>
17711771
if (← read).errToSorry && ex matches .error .. then
1772-
exceptionToSorry ex expectedType?
1772+
withRef stx <| exceptionToSorry ex expectedType?
17731773
else
17741774
throw ex
17751775

src/Lean/Meta/AppBuilder.lean

+3-3
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ def mkSome (type value : Expr) : MetaM Expr := do
514514
return mkApp2 (mkConst ``Option.some [u]) type value
515515

516516
/--
517-
Return `sorryAx type synthetic`. Recall that `synthetic` is true if this sorry is from an error.
517+
Returns `sorryAx type synthetic`. Recall that `synthetic` is true if this sorry is from an error.
518518
-/
519519
def mkSorry (type : Expr) (synthetic : Bool) : MetaM Expr := do
520520
let u ← getLevel type
@@ -544,7 +544,7 @@ def UniqueSorryView.decode? (name : Name) : Option UniqueSorryView := do
544544
failure
545545

546546
/--
547-
Make a `sorryAx` that is unique, in the sense that it is not defeq to any other `sorry` created by `mkUniqueSorry`.
547+
Makes a `sorryAx` that is unique, in the sense that it is not defeq to any other `sorry` created by `mkUniqueSorry`.
548548
549549
Encodes the source position of the current ref into the term.
550550
-/
@@ -559,7 +559,7 @@ def mkUniqueSorry (type : Expr) (synthetic : Bool) : MetaM Expr := do
559559
return .app e (toExpr tag)
560560

561561
/--
562-
Return a `UniqueSorryView` if `e` is an application of an expression returned by `mkUniqueSorry`.
562+
Returns a `UniqueSorryView` if `e` is an application of an expression returned by `mkUniqueSorry`.
563563
-/
564564
def isUniqueSorry? (e : Expr) : Option UniqueSorryView := do
565565
guard <| e.isAppOf ``sorryAx && e.getAppNumArgs ≥ 3

src/Lean/Meta/Tactic/Util.lean

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def _root_.Lean.MVarId.admit (mvarId : MVarId) (synthetic := true) : MetaM Unit
6060
mvarId.withContext do
6161
mvarId.checkNotAssigned `admit
6262
let mvarType ← mvarId.getType
63-
let val ← mkSorry mvarType synthetic
63+
let val ← mkUniqueSorry mvarType synthetic
6464
mvarId.assign val
6565

6666
/-- Beta reduce the metavariable type head -/

src/Lean/Parser/Term.lean

+5-3
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,13 @@ def binderIdent : Parser := ident <|> hole
218218
/--
219219
The `sorry` term is a temporary placeholder for a missing proof or value.
220220
221-
This is intended for stubbing out incomplete parts of a proof while still having a syntactically correct proof skeleton.
222-
Lean will give a warning whenever a proof uses `sorry`, so you aren't likely to miss it,
223-
but you can double check if a theorem depends on `sorry` by looking for `sorryAx` in the output
221+
The syntax is intended for stubbing-out incomplete parts of a value or proof while still having a syntactically correct skeleton.
222+
Lean will give a warning whenever a declaration uses `sorry`, so you aren't likely to miss it,
223+
but you can double check if a declaration depends on `sorry` by looking for `sorryAx` in the output
224224
of the `#print axioms my_thm` command, which is the axiom used by implementation of `sorry`.
225225
226+
"Go to definition" on `sorry` will go to the source position where it was introduced.
227+
226228
Each `sorry` is guaranteed to be unique, so for example the following fails:
227229
```lean
228230
example : (sorry : Nat) = sorry := rfl -- fails

src/Lean/PrettyPrinter/Delaborator/Basic.lean

+24-4
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ where
212212
}
213213

214214
/--
215-
Annotates the term with the current expression position and registers `TermInfo`
215+
Annotate the term with the current expression position and register `TermInfo`
216216
to associate the term to the current expression.
217217
-/
218218
def annotateTermInfo (stx : Term) : Delab := do
@@ -221,13 +221,31 @@ def annotateTermInfo (stx : Term) : Delab := do
221221
pure stx
222222

223223
/--
224-
Modifies the delaborator so that it annotates the resulting term with the current expression
225-
position and registers `TermInfo` to associate the term to the current expression.
224+
Annotates the term with the current expression position and registers `TermInfo`
225+
to associate the term to the current expression, unless (1) the syntax has a position and it is for the current position
226+
and (2) there is already `TermInfo` at this position.
227+
-/
228+
def annotateTermInfoUnlessAnnotated (stx : Term) : Delab := do
229+
if let .synthetic ⟨pos⟩ ⟨pos'⟩ := stx.raw.getHeadInfo then
230+
if pos == pos' && pos == (← getPos) then
231+
if (← get).infos.contains pos then
232+
return stx
233+
annotateTermInfo stx
234+
235+
/--
236+
Modifies the delaborator so that it annotates the resulting term using `annotateTermInfo`.
226237
-/
227238
def withAnnotateTermInfo (d : Delab) : Delab := do
228239
let stx ← d
229240
annotateTermInfo stx
230241

242+
/--
243+
Modifies the delaborator so that it ensures resulting term is annotated using `annotateTermInfoUnlessAnnotated`.
244+
-/
245+
def withAnnotateTermInfoUnlessAnnotated (d : Delab) : Delab := do
246+
let stx ← d
247+
annotateTermInfoUnlessAnnotated stx
248+
231249
def getUnusedName (suggestion : Name) (body : Expr) : DelabM Name := do
232250
-- Use a nicer binder name than `[anonymous]`. We probably shouldn't do this in all LocalContext use cases, so do it here.
233251
let suggestion := if suggestion.isAnonymous then `a else suggestion
@@ -271,11 +289,13 @@ inductive OmissionReason
271289
| deep
272290
| proof
273291
| maxSteps
292+
| uniqueSorry
274293

275294
def OmissionReason.toString : OmissionReason → String
276295
| deep => "Term omitted due to its depth (see option `pp.deepTerms`)."
277296
| proof => "Proof omitted (see option `pp.proofs`)."
278297
| maxSteps => "Term omitted due to reaching the maximum number of steps allowed for pretty printing this expression (see option `pp.maxSteps`)."
298+
| uniqueSorry => "This is a `sorry` term associated to a source position. Use 'Go to definition' to go there."
279299

280300
def addOmissionInfo (pos : Pos) (stx : Syntax) (e : Expr) (reason : OmissionReason) : DelabM Unit := do
281301
let info := Info.ofOmissionInfo <| ← mkOmissionInfo stx e
@@ -365,7 +385,7 @@ def omission (reason : OmissionReason) : Delab := do
365385
partial def delabFor : Name → Delab
366386
| Name.anonymous => failure
367387
| k =>
368-
(do annotateTermInfo (← (delabAttribute.getValues (← getEnv) k).firstM id))
388+
(do annotateTermInfoUnlessAnnotated (← (delabAttribute.getValues (← getEnv) k).firstM id))
369389
-- have `app.Option.some` fall back to `app` etc.
370390
<|> if k.isAtomic then failure else delabFor k.getRoot
371391

src/Lean/PrettyPrinter/Delaborator/Builtins.lean

+23-7
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ def withOverApp (arity : Nat) (x : Delab) : Delab := do
573573
else
574574
let delabHead (insertExplicit : Bool) : Delab := do
575575
guard <| !insertExplicit
576-
withAnnotateTermInfo x
576+
withAnnotateTermInfoUnlessAnnotated x
577577
delabAppCore (n - arity) delabHead (unexpand := false)
578578

579579
@[builtin_delab app]
@@ -1227,14 +1227,30 @@ def delabNameMkStr : Delab := whenPPOption getPPNotation do
12271227
def delabNameMkNum : Delab := delabNameMkStr
12281228

12291229
@[builtin_delab app.sorryAx]
1230-
def delabSorry : Delab := whenPPOption getPPNotation do
1230+
def delabSorry : Delab := whenPPOption getPPNotation <| whenNotPPOption getPPExplicit do
12311231
guard <| (← getExpr).getAppNumArgs ≥ 2
1232-
let mut arity := 2
12331232
-- If this is constructed by `Lean.Meta.mkUniqueSorry`, then don't print the unique tag.
1234-
if isUniqueSorry? (← getExpr) |>.isSome then
1235-
arity := 3
1236-
withOverApp arity do
1237-
`(sorry)
1233+
-- But, if `pp.explicit` is false and `pp.sorrySource` is true, then print a simplified version of the tag.
1234+
if let some view := isUniqueSorry? (← getExpr) then
1235+
withOverApp 3 do
1236+
if let (some module, some range) := (view.module?, view.range?) then
1237+
if ← getPPOption getPPSorrySource then
1238+
-- LSP line numbers start at 0, so add one to it.
1239+
-- Technically using the character as the column is incorrect since this is UTF-16 position, but we have no filemap to work with.
1240+
let posAsName := Name.mkSimple s!"{module}:{range.start.line + 1}:{range.start.character}"
1241+
let pos := mkNode ``Lean.Parser.Term.quotedName #[Syntax.mkNameLit s!"`{posAsName}"]
1242+
let src ← withAppArg <| annotateTermInfo pos
1243+
`(sorry $src)
1244+
else
1245+
-- Hack: use omission info so that the first hover gives the sorry source.
1246+
let stx ← `(sorry)
1247+
let stx ← annotateCurPos stx
1248+
addOmissionInfo (← getPos) stx (← getExpr) .uniqueSorry
1249+
return stx
1250+
else
1251+
`(sorry)
1252+
else
1253+
withOverApp 2 `(sorry)
12381254

12391255
open Parser Command Term in
12401256
@[run_builtin_parser_attribute_hooks]

src/Lean/PrettyPrinter/Delaborator/Options.lean

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ register_builtin_option pp.match : Bool := {
3434
group := "pp"
3535
descr := "(pretty printer) disable/enable 'match' notation"
3636
}
37+
register_builtin_option pp.sorrySource : Bool := {
38+
defValue := false
39+
group := "pp"
40+
descr := "(pretty printer) if true, pretty print 'sorry' with its originating source position, if available"
41+
}
3742
register_builtin_option pp.coercions : Bool := {
3843
defValue := true
3944
group := "pp"
@@ -250,6 +255,7 @@ def getPPExplicit (o : Options) : Bool := o.get pp.explicit.name (getPPAll o)
250255
def getPPNotation (o : Options) : Bool := o.get pp.notation.name (!getPPAll o)
251256
def getPPUnicodeFun (o : Options) : Bool := o.get pp.unicode.fun.name false
252257
def getPPMatch (o : Options) : Bool := o.get pp.match.name (!getPPAll o)
258+
def getPPSorrySource (o : Options) : Bool := o.get pp.sorrySource.name pp.sorrySource.defValue
253259
def getPPFieldNotation (o : Options) : Bool := o.get pp.fieldNotation.name (!getPPAll o)
254260
def getPPFieldNotationGeneralized (o : Options) : Bool := o.get pp.fieldNotation.generalized.name pp.fieldNotation.generalized.defValue
255261
def getPPStructureInstances (o : Options) : Bool := o.get pp.structureInstances.name (!getPPAll o)

src/Lean/Server/FileWorker/RequestHandling.lean

+17-9
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,17 @@ def locationLinksOfInfo (kind : GoToKind) (ictx : InfoWithCtx)
151151
let i := ictx.info
152152
let ci := ictx.ctx
153153
let children := ictx.children
154-
match i with
155-
| .ofTermInfo ti =>
154+
155+
let locationLinksDefault : RequestM (Array LocationLink) := do
156+
-- If other go-tos fail, we try to show the elaborator or parser
157+
if let some ei := i.toElabInfo? then
158+
if kind == declaration && ci.env.contains ei.stx.getKind then
159+
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.stx.getKind
160+
if kind == definition && ci.env.contains ei.elaborator then
161+
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.elaborator
162+
return #[]
163+
164+
let locationLinksFromTermInfo (ti : TermInfo) : RequestM (Array LocationLink) := do
156165
let mut expr := ti.expr
157166
if kind == type then
158167
expr ← ci.runMetaM i.lctx do
@@ -206,6 +215,11 @@ def locationLinksOfInfo (kind : GoToKind) (ictx : InfoWithCtx)
206215
}
207216
results := results.insertAt 0 result
208217
return results
218+
locationLinksDefault
219+
220+
match i with
221+
| .ofTermInfo ti => return ← locationLinksFromTermInfo ti
222+
| .ofOmissionInfo ti => return ← locationLinksFromTermInfo ti.toTermInfo
209223
| .ofFieldInfo fi =>
210224
if kind == type then
211225
let expr ← ci.runMetaM i.lctx do
@@ -220,13 +234,7 @@ def locationLinksOfInfo (kind : GoToKind) (ictx : InfoWithCtx)
220234
if kind == definition || kind == declaration then
221235
return ← ci.runMetaM i.lctx <| locationLinksFromImport i
222236
| _ => pure ()
223-
-- If other go-tos fail, we try to show the elaborator or parser
224-
if let some ei := i.toElabInfo? then
225-
if kind == declaration && ci.env.contains ei.stx.getKind then
226-
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.stx.getKind
227-
if kind == definition && ci.env.contains ei.elaborator then
228-
return ← ci.runMetaM i.lctx <| locationLinksFromDecl i ei.elaborator
229-
return #[]
237+
locationLinksDefault
230238

231239
open Elab GoToKind in
232240
def handleDefinition (kind : GoToKind) (p : TextDocumentPositionParams)

src/Lean/Widget/InteractiveCode.lean

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def ppExprTagged (e : Expr) (explicit : Bool := false) : MetaM CodeWithInfos :=
8484
delabApp
8585
else
8686
withOptionAtCurrPos pp.proofs.name true do
87+
withOptionAtCurrPos pp.sorrySource.name true do
8788
delab
8889
let mut e := e
8990
-- When hovering over a metavariable, we want to see its value, even if `pp.instantiateMVars` is false.

0 commit comments

Comments
 (0)