lean4-htt/src/Lean/Compiler/ExternAttr.lean
2022-07-24 17:44:29 -07:00

162 lines
6 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Expr
import Lean.Environment
import Lean.Attributes
import Lean.ProjFns
import Lean.Meta.Basic
namespace Lean
inductive ExternEntry where
| adhoc (backend : Name)
| inline (backend : Name) (pattern : String)
| standard (backend : Name) (fn : String)
| foreign (backend : Name) (fn : String)
/--
- `@[extern]`
encoding: ```.entries = [adhoc `all]```
- `@[extern "level_hash"]`
encoding: ```.entries = [standard `all "levelHash"]```
- `@[extern cpp "lean::string_size" llvm "lean_str_size"]`
encoding: ```.entries = [standard `cpp "lean::string_size", standard `llvm "leanStrSize"]```
- `@[extern cpp inline "#1 + #2"]`
encoding: ```.entries = [inline `cpp "#1 + #2"]```
- `@[extern cpp "foo" llvm adhoc]`
encoding: ```.entries = [standard `cpp "foo", adhoc `llvm]```
- `@[extern 2 cpp "io_prim_println"]`
encoding: ```.arity? = 2, .entries = [standard `cpp "ioPrimPrintln"]```
-/
structure ExternAttrData where
arity? : Option Nat := none
entries : List ExternEntry
deriving Inhabited
-- def externEntry := leading_parser optional ident >> optional (nonReservedSymbol "inline ") >> strLit
-- @[builtinAttrParser] def extern := leading_parser nonReservedSymbol "extern " >> optional numLit >> many externEntry
private def syntaxToExternAttrData (stx : Syntax) : AttrM ExternAttrData := do
let arity? := if stx[1].isNone then none else some <| stx[1][0].isNatLit?.getD 0
let entriesStx := stx[2].getArgs
if entriesStx.size == 0 && arity? == none then
return { entries := [ ExternEntry.adhoc `all ] }
let mut entries := #[]
for entryStx in entriesStx do
let backend := if entryStx[0].isNone then `all else entryStx[0][0].getId
let str ← match entryStx[2].isStrLit? with
| none => throwErrorAt entryStx[2] "string literal expected"
| some str => pure str
if entryStx[1].isNone then
entries := entries.push <| ExternEntry.standard backend str
else
entries := entries.push <| ExternEntry.inline backend str
return { arity? := arity?, entries := entries.toList }
@[extern "lean_add_extern"]
opaque addExtern (env : Environment) (n : Name) : ExceptT String Id Environment
builtin_initialize externAttr : ParametricAttribute ExternAttrData ←
registerParametricAttribute {
name := `extern
descr := "builtin and foreign functions"
getParam := fun _ stx => syntaxToExternAttrData stx
afterSet := fun declName _ => do
let mut env ← getEnv
if env.isProjectionFn declName || env.isConstructor declName then do
env ← ofExcept <| addExtern env declName
setEnv env
else
pure ()
}
@[export lean_get_extern_attr_data]
def getExternAttrData? (env : Environment) (n : Name) : Option ExternAttrData :=
externAttr.getParam? env n
private def parseOptNum : Nat → String.Iterator → Nat → String.Iterator × Nat
| 0, it, r => (it, r)
| n+1, it, r =>
if !it.hasNext then (it, r)
else
let c := it.curr
if '0' <= c && c <= '9'
then parseOptNum n it.next (r*10 + (c.toNat - '0'.toNat))
else (it, r)
def expandExternPatternAux (args : List String) : Nat → String.Iterator → String → String
| 0, _, r => r
| i+1, it, r =>
if ¬ it.hasNext then r
else let c := it.curr
if c ≠ '#' then expandExternPatternAux args i it.next (r.push c)
else
let it := it.next
let (it, j) := parseOptNum it.remainingBytes it 0
let j := j-1
expandExternPatternAux args i it (r ++ args.getD j "")
def expandExternPattern (pattern : String) (args : List String) : String :=
expandExternPatternAux args pattern.length pattern.mkIterator ""
def mkSimpleFnCall (fn : String) (args : List String) : String :=
fn ++ "(" ++ ((args.intersperse ", ").foldl (·++·) "") ++ ")"
def ExternEntry.backend : ExternEntry → Name
| ExternEntry.adhoc n => n
| ExternEntry.inline n _ => n
| ExternEntry.standard n _ => n
| ExternEntry.foreign n _ => n
def getExternEntryForAux (backend : Name) : List ExternEntry → Option ExternEntry
| [] => none
| e::es =>
if e.backend == `all then some e
else if e.backend == backend then some e
else getExternEntryForAux backend es
def getExternEntryFor (d : ExternAttrData) (backend : Name) : Option ExternEntry :=
getExternEntryForAux backend d.entries
def isExtern (env : Environment) (fn : Name) : Bool :=
getExternAttrData? env fn |>.isSome
/-- We say a Lean function marked as `[extern "<c_fn_nane>"]` is for all backends, and it is implemented using `extern "C"`.
Thus, there is no name mangling. -/
def isExternC (env : Environment) (fn : Name) : Bool :=
match getExternAttrData? env fn with
| some { entries := [ ExternEntry.standard `all _ ], .. } => true
| _ => false
def getExternNameFor (env : Environment) (backend : Name) (fn : Name) : Option String := do
let data? ← getExternAttrData? env fn
let entry ← getExternEntryFor data? backend
match entry with
| ExternEntry.standard _ n => pure n
| ExternEntry.foreign _ n => pure n
| _ => failure
private def getExternConstArity (declName : Name) : CoreM Nat := do
let fromSignature : Unit → CoreM Nat := fun _ => do
let cinfo ← getConstInfo declName
let (arity, _) ← (Meta.forallTelescopeReducing cinfo.type fun xs _ => pure xs.size : MetaM Nat).run
return arity
let env ← getEnv
match getExternAttrData? env declName with
| none => fromSignature ()
| some data => match data.arity? with
| some arity => return arity
| none => fromSignature ()
@[export lean_get_extern_const_arity]
def getExternConstArityExport (env : Environment) (declName : Name) : IO (Option Nat) := do
try
let (arity, _) ← (getExternConstArity declName).toIO { fileName := "<compiler>", fileMap := default } { env := env }
return some arity
catch
| IO.Error.userError _ => return none
| _ => return none
end Lean