Compare commits

...

5 Commits

Author SHA1 Message Date
Sebastian Ullrich
23f7fdc19c fix coeFun/Sort infinite recursion 2024-02-09 12:58:11 +01:00
Sebastian Ullrich
2b1156eeae fix coeDelaborator priority 2024-02-09 12:53:13 +01:00
Leonardo de Moura
c7f3b2988f refactor: [coe] attribute, coeFunNotation, and coeSortNotation 2024-02-08 19:34:45 -08:00
Scott Morrison
17952b5955 chore: upstream Std.Tactic.CoeExt 2024-02-08 15:39:03 +11:00
Scott Morrison
79db442dfd chore: upstream Std.Tactic.CoeExt 2024-02-08 15:37:00 +11:00
9 changed files with 160 additions and 8 deletions

View File

@@ -290,6 +290,12 @@ between e.g. `↑x + ↑y` and `↑(x + y)`.
-/
syntax:1024 (name := coeNotation) "" term:1024 : term
/-- `⇑ t` coerces `t` to a function. -/
syntax:1024 (name := coeFunNotation) "" term:1024 : term
/-- `↥ t` coerces `t` to a type. -/
syntax:1024 (name := coeSortNotation) "" term:1024 : term
/-! # Basic instances -/
instance boolToProp : Coe Bool Prop where

View File

@@ -484,6 +484,13 @@ existing code. It may be removed in a future version of the library.
-/
syntax (name := deprecated) "deprecated" (ppSpace ident)? : attr
/--
The `@[coe]` attribute on a function (which should also appear in a
`instance : Coe A B := ⟨myFn⟩` declaration) allows the delaborator to show
applications of this function as `↑` when printing expressions.
-/
syntax (name := Attr.coe) "coe" : attr
/--
When `parent_dir` contains the current Lean file, `include_str "path" / "to" / "file"` becomes
a string literal with the contents of the file at `"parent_dir" / "path" / "to" / "file"`. If this

View File

@@ -19,6 +19,22 @@ open Meta
throwError "invalid coercion notation, expected type is not known"
ensureHasType expectedType? e
@[builtin_term_elab coeFunNotation] def elabCoeFunNotation : TermElab := fun stx _ => do
let stx := stx[1]
let x elabTerm stx none
if let some ty coerceToFunction? x then
return ty
else
throwError "cannot coerce to function{indentExpr x}"
@[builtin_term_elab coeSortNotation] def elabCoeSortNotation : TermElab := fun stx _ => do
let stx := stx[1]
let x elabTerm stx none
if let some ty coerceToSort? x then
return ty
else
throwError "cannot coerce to sort{indentExpr x}"
@[builtin_term_elab anonymousCtor] def elabAnonymousCtor : TermElab := fun stx expectedType? =>
match stx with
| `($args,*) => do

View File

@@ -43,3 +43,4 @@ import Lean.Meta.CasesOn
import Lean.Meta.ExprLens
import Lean.Meta.ExprTraverse
import Lean.Meta.Eval
import Lean.Meta.CoeAttr

View File

@@ -0,0 +1,86 @@
/-
Copyright (c) 2022 Gabriel Ebner. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner, Mario Carneiro, Leonardo de Moura
-/
import Lean.Attributes
import Lean.ScopedEnvExtension
import Lean.Meta.FunInfo
/-!
# The `@[coe]` attribute, used to delaborate coercion functions as `↑`
When writing a coercion, if the pattern
```
@[coe]
def A.toB (a : A) : B := sorry
instance : Coe A B where coe := A.toB
```
is used, then `A.toB a` will be pretty-printed as `↑a`.
-/
namespace Lean.Meta
/-- The different types of coercions that are supported by the `coe` attribute. -/
inductive CoeFnType
/-- The basic coercion `↑x`, see `CoeT.coe` -/
| coe
/-- The coercion to a function type, see `CoeFun.coe` -/
| coeFun
/-- The coercion to a type, see `CoeSort.coe` -/
| coeSort
deriving Inhabited, Repr, DecidableEq
instance : ToExpr CoeFnType where
toTypeExpr := mkConst ``CoeFnType
toExpr := open CoeFnType in fun
| coe => mkConst ``coe
| coeFun => mkConst ``coeFun
| coeSort => mkConst ``coeSort
/-- Information associated to a coercion function to enable sensible delaboration. -/
structure CoeFnInfo where
/-- The number of arguments to the coercion function -/
numArgs : Nat
/-- The argument index that represents the value being coerced -/
coercee : Nat
/-- The type of coercion -/
type : CoeFnType
deriving Inhabited, Repr
instance : ToExpr CoeFnInfo where
toTypeExpr := mkConst ``CoeFnInfo
toExpr | a, b, c => mkApp3 (mkConst ``CoeFnInfo.mk) (toExpr a) (toExpr b) (toExpr c)
/-- The environment extension for tracking coercion functions for delaboration -/
-- TODO: does it need to be a scoped extension
initialize coeExt : SimpleScopedEnvExtension (Name × CoeFnInfo) (NameMap CoeFnInfo)
registerSimpleScopedEnvExtension {
addEntry := fun st (n, i) => st.insert n i
initial := {}
}
/-- Lookup the coercion information for a given function -/
def getCoeFnInfo? (fn : Name) : CoreM (Option CoeFnInfo) :=
return (coeExt.getState ( getEnv)).find? fn
/-- Add `name` to the coercion extension and add a coercion delaborator for the function. -/
def registerCoercion (name : Name) (info : Option CoeFnInfo := none) : MetaM Unit := do
let info match info with | some info => pure info | none => do
let fnInfo getFunInfo ( mkConstWithLevelParams name)
let some coercee := fnInfo.paramInfo.findIdx? (·.binderInfo.isExplicit)
| throwError "{name} has no explicit arguments"
pure { numArgs := coercee + 1, coercee, type := .coe }
modifyEnv fun env => coeExt.addEntry env (name, info)
builtin_initialize registerBuiltinAttribute {
name := `coe
descr := "Adds a definition as a coercion"
add := fun decl _stx kind => MetaM.run' do
unless kind == .global do
throwError "cannot add local or scoped coe attribute"
registerCoercion decl
}
end Lean.Meta

View File

@@ -1,13 +1,13 @@
/-
Copyright (c) 2020 Sebastian Ullrich. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich
Authors: Sebastian Ullrich, Leonardo de Moura, Gabriel Ebner, Mario Carneiro
-/
import Lean.Parser
import Lean.PrettyPrinter.Delaborator.Basic
import Lean.PrettyPrinter.Delaborator.SubExpr
import Lean.PrettyPrinter.Delaborator.TopDownAnalyze
import Lean.Parser
import Lean.Meta.CoeAttr
namespace Lean.PrettyPrinter.Delaborator
open Lean.Meta
@@ -315,7 +315,8 @@ Default delaborator for applications.
-/
@[builtin_delab app]
def delabApp : Delab := do
delabAppCore ( getExpr).getAppNumArgs delabAppFn
let e getExpr
delabAppCore e.getAppNumArgs delabAppFn
/--
The `withOverApp` combinator allows delaborators to handle "over-application" by using the core
@@ -810,6 +811,28 @@ def delabProjectionApp : Delab := whenPPOption getPPStructureProjections $ do
let appStx withAppArg delab
`($(appStx).$(mkIdent f):ident)
/--
This delaborator tries to elide functions which are known coercions.
For example, `Int.ofNat` is a coercion, so instead of printing `ofNat n` we just print `↑n`,
and when re-parsing this we can (usually) recover the specific coercion being used.
-/
-- NOTE: should take precedence over, i.e. be declared below, `delabProjectionApp`
@[builtin_delab app]
def coeDelaborator : Delab := whenPPOption getPPCoercions do
let e getExpr
let .const declName _ := e.getAppFn | failure
let some info Meta.getCoeFnInfo? declName | failure
let n := e.getAppNumArgs
withOverApp info.numArgs do
match info.type with
| .coe => `($( withNaryArg info.coercee delab))
| .coeFun =>
if n = info.numArgs then
`($( withNaryArg info.coercee delab))
else
withNaryArg info.coercee delab
| .coeSort => `($( withNaryArg info.coercee delab))
@[builtin_delab app.dite]
def delabDIte : Delab := whenPPOption getPPNotation <| withOverApp 5 do
-- Note: we keep this as a delaborator for now because it actually accesses the expression.

View File

@@ -1,8 +1,8 @@
some { range := { pos := { line := 175, column := 42 },
some { range := { pos := { line := 189, column := 42 },
charUtf16 := 42,
endPos := { line := 181, column := 31 },
endPos := { line := 195, column := 31 },
endCharUtf16 := 31 },
selectionRange := { pos := { line := 175, column := 46 },
selectionRange := { pos := { line := 189, column := 46 },
charUtf16 := 46,
endPos := { line := 175, column := 58 },
endPos := { line := 189, column := 58 },
endCharUtf16 := 58 } }

11
tests/lean/coeAttr1.lean Normal file
View File

@@ -0,0 +1,11 @@
@[coe] def f (n : Nat) : String :=
toString n
#check f 10
instance : Coe Nat String where
coe := f
def g (n : String) := n
#check fun x : Nat => g x

View File

@@ -0,0 +1,2 @@
↑10 : String
fun x => g ↑x : Nat → String