lean4-htt/src/Lean/Compiler/ExternAttr.lean
Markus Himmel fa67f300f6
chore: rename String.ValidPos to String.Pos (#11240)
This PR renames `String.ValidPos` to `String.Pos`, `String.endValidPos`
to `String.endPos` and `String.startValidPos` to `String.startPos`.

Accordingly, the deprecations of `String.Pos` to `String.Pos.Raw` and
`String.endPos` to `String.rawEndPos` are removed early, after an
abbreviated deprecation cycle of two releases.
2025-11-24 16:40:21 +00:00

137 lines
5.1 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
-/
module
prelude
public import Lean.ProjFns
public import Lean.Attributes
public section
namespace Lean
inductive ExternEntry where
| adhoc (backend : Name)
| inline (backend : Name) (pattern : String)
| standard (backend : Name) (fn : String)
/-- Call to a Lean function without exported IR. -/
| opaque
deriving BEq, Hashable
/--
- `@[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]```
-/
structure ExternAttrData where
entries : List ExternEntry
deriving Inhabited, BEq, Hashable
-- def externEntry := leading_parser optional ident >> optional (nonReservedSymbol "inline ") >> strLit
-- @[builtin_attr_parser] def extern := leading_parser nonReservedSymbol "extern " >> optional numLit >> many externEntry
private def syntaxToExternAttrData (stx : Syntax) : AttrM ExternAttrData := do
let entriesStx := stx[1].getArgs
if entriesStx.size == 0 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 { entries := entries.toList }
-- Forward declaration
@[extern "lean_add_extern"]
opaque addExtern (declName : Name) (externAttrData : ExternAttrData) : CoreM Unit
builtin_initialize externAttr : ParametricAttribute ExternAttrData ←
registerParametricAttribute {
name := `extern
descr := "builtin and foreign functions"
getParam := fun _ stx => syntaxToExternAttrData stx
afterSet := fun declName externAttrData => do
let env ← getEnv
if env.isProjectionFn declName || env.isConstructor declName then
if let some (.thmInfo ..) := env.find? declName then
-- We should not mark theorems as extern
return ()
addExtern declName externAttrData
}
def getExternAttrData? (env : Environment) (n : Name) : Option ExternAttrData :=
externAttr.getParam? env n
private def parseOptNum : Nat → (pattern : String) → (it : pattern.Pos) → Nat → pattern.Pos × Nat
| 0, _ , it, r => (it, r)
| n+1, pattern, it, r =>
if h : it.IsAtEnd then (it, r)
else
let c := it.get h
if '0' <= c && c <= '9'
then parseOptNum n pattern (it.next h) (r*10 + (c.toNat - '0'.toNat))
else (it, r)
def expandExternPatternAux (args : List String) : Nat → (pattern : String) → (it : pattern.Pos) → String → String
| 0, _, _, r => r
| i+1, pattern, it, r =>
if h : it.IsAtEnd then r
else let c := it.get h
if c ≠ '#' then expandExternPatternAux args i pattern (it.next h) (r.push c)
else
let it := it.next h
let (it, j) := parseOptNum it.remainingBytes pattern it 0
let j := j-1
expandExternPatternAux args i pattern it (r ++ args.getD j "")
def expandExternPattern (pattern : String) (args : List String) : String :=
expandExternPatternAux args pattern.length pattern pattern.startPos ""
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.opaque .. => `all
def getExternEntryForAux (backend : Name) (entries : List ExternEntry) : Option ExternEntry :=
entries.find? fun e =>
e.backend == `all || e.backend == backend
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_name>"]` 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
| _ => failure
end Lean