Skip to content

Commit 016c6c7

Browse files
Encode entry-function invariants on Source.Function (#424)
Add two propositional fields to `Source.Function`: - `entryMonomorphic : params = [] ∨ entry = false` - `entryPointerFree : sigPointerFree inputs output = true ∨ entry = false` `Typ.hasPointer` + `sigPointerFree` are new helpers. Smart constructors `monoNonEntry`, `monoEntry`, and `poly` discharge both props by construction. `Meta.elabFunction` dispatches via the `pub` keyword and uses `mkDecideProof` on `sigPointerFree` to reject pointer-typed entry signatures at parse time. `Check.mkDecls` re-derives the proof after alias expansion and surfaces a new `CheckError.entryHasPointer` if an alias hides a pointer in an entry signature.
1 parent 7bdeed9 commit 016c6c7

3 files changed

Lines changed: 99 additions & 18 deletions

File tree

Ix/Aiur/Compiler/Check.lean

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ inductive CheckError
4949
| infiniteType : Nat → Typ → CheckError
5050
| unresolvedMVar : Nat → CheckError
5151
| u8LitOutOfRange : Nat → CheckError
52+
| entryHasPointer : Global → CheckError
5253
deriving Repr
5354

5455
instance : ToString CheckError where
@@ -178,7 +179,15 @@ def Source.Toplevel.mkDecls (toplevel : Source.Toplevel) : Except CheckError Sou
178179
let typ' ← expandTyp typ
179180
pure (loc, typ')
180181
let output' ← expandTyp function.output
181-
let function' := { function with inputs := inputs', output := output' }
182+
let function' : Source.Function ←
183+
match hEntry : function.entry with
184+
| true =>
185+
if hSig : sigPointerFree inputs' output' = true then
186+
pure { function with inputs := inputs', output := output', entryPointerFree := Or.inl hSig }
187+
else
188+
throw $ .entryHasPointer function.name
189+
| false =>
190+
pure { function with inputs := inputs', output := output', entryPointerFree := Or.inr hEntry }
182191
decls := decls.insert function.name (.function function')
183192

184193
for dataType in toplevel.dataTypes do

Ix/Aiur/Meta.lean

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -654,45 +654,59 @@ def elabFunction : ElabStxCat `aiur_function
654654
| `(aiur_function| $[pub%$e]? fn $i:ident() $[-> $ty:aiur_typ]? {$t:aiur_trm}) => do
655655
let g ← mkAppM ``Global.mk #[toExpr i.getId]
656656
let bindType ← mkAppM ``Prod #[mkConst ``Local, mkConst ``Typ]
657-
let e := elabEntryBool e
658-
mkAppM ``Source.Function.mk
659-
#[g, ← elabEmptyList ``String, ← mkListLit bindType [],
660-
← elabRetTyp ty, ← elabTrm t, e]
657+
let inputs ← mkListLit bindType []
658+
let output ← elabRetTyp ty
659+
let body ← elabTrm t
660+
mkMonoFun e g inputs output body
661661
| `(aiur_function| $[pub%$e]? fn $i:ident($b:aiur_bind $[, $bs:aiur_bind]*)
662662
$[-> $ty:aiur_typ]? {$t:aiur_trm}) => do
663663
let g ← mkAppM ``Global.mk #[toExpr i.getId]
664664
let bindType ← mkAppM ``Prod #[mkConst ``Local, mkConst ``Typ]
665-
let e := elabEntryBool e
666-
mkAppM ``Source.Function.mk
667-
#[g, ← elabEmptyList ``String,
668-
← elabListCore b bs elabBind bindType,
669-
← elabRetTyp ty, ← elabTrm t, e]
665+
let inputs ← elabListCore b bs elabBind bindType
666+
let output ← elabRetTyp ty
667+
let body ← elabTrm t
668+
mkMonoFun e g inputs output body
670669
| `(aiur_function| fn $i:ident‹$p:ident $[, $ps:ident]*›()
671670
$[-> $ty:aiur_typ]? {$t:aiur_trm}) => do
672671
let g ← mkAppM ``Global.mk #[toExpr i.getId]
673672
let (_, paramsExpr) ← elabTypeParams p ps
674673
let bindType ← mkAppM ``Prod #[mkConst ``Local, mkConst ``Typ]
675-
mkAppM ``Source.Function.mk
674+
mkAppM ``Source.Function.poly
676675
#[g, paramsExpr, ← mkListLit bindType [],
677-
← elabRetTyp ty, ← elabTrm t, mkConst ``Bool.false]
676+
← elabRetTyp ty, ← elabTrm t]
678677
| `(aiur_function| fn $i:ident‹$p:ident $[, $ps:ident]*›
679678
($b:aiur_bind $[, $bs:aiur_bind]*)
680679
$[-> $ty:aiur_typ]? {$t:aiur_trm}) => do
681680
let g ← mkAppM ``Global.mk #[toExpr i.getId]
682681
let (_, paramsExpr) ← elabTypeParams p ps
683682
let bindType ← mkAppM ``Prod #[mkConst ``Local, mkConst ``Typ]
684-
mkAppM ``Source.Function.mk
683+
mkAppM ``Source.Function.poly
685684
#[g, paramsExpr,
686685
← elabListCore b bs elabBind bindType,
687-
← elabRetTyp ty, ← elabTrm t, mkConst ``Bool.false]
686+
← elabRetTyp ty, ← elabTrm t]
688687
| stx => throw $ .error stx "Invalid syntax for function"
689688
where
690-
elabEntryBool : Option Syntax → Expr
691-
| none => mkConst ``Bool.false
692-
| some _ => mkConst ``Bool.true
693689
elabRetTyp : Option (TSyntax `aiur_typ) → TermElabM Expr
694690
| none => pure $ mkConst ``Typ.unit
695691
| some typ => elabTyp typ
692+
/-- Build a monomorphic `Source.Function`. If `pubKw?` is `some _`
693+
(public/entry function), require a `sigPointerFree` proof: signatures
694+
with `.pointer` types are rejected with a Meta-level error. -/
695+
mkMonoFun (pubKw? : Option Syntax) (name inputs output body : Expr) :
696+
TermElabM Expr := do
697+
match pubKw? with
698+
| none =>
699+
mkAppM ``Source.Function.monoNonEntry #[name, inputs, output, body]
700+
| some kw =>
701+
let sigExpr ← mkAppM ``Source.sigPointerFree #[inputs, output]
702+
let propExpr ← mkAppM ``Eq #[sigExpr, mkConst ``Bool.true]
703+
let proof ←
704+
try
705+
Lean.Meta.mkDecideProof propExpr
706+
catch _ =>
707+
throw $ .error kw
708+
"Public (entry) function signatures cannot contain pointer types"
709+
mkAppM ``Source.Function.monoEntry #[name, inputs, output, body, proof]
696710

697711
declare_syntax_cat aiur_declaration
698712
syntax aiur_function : aiur_declaration

Ix/Aiur/Stages/Source.lean

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,23 @@ theorem eq_of_beq {a b : Typ} (h : beq a b = true) : a = b := by
332332
have hk : k < tl.length := by simp [List.length] at h; omega
333333
exact ihTl.2 k hk t' hb
334334

335+
/-- Does `t` contain any `.pointer` subterm? Used to forbid pointer types in
336+
the signatures of `entry = true` (public) functions. -/
337+
def hasPointer : Typ → Bool
338+
| .unit | .field | .u8 | .ref _ | .mvar _ => false
339+
| .pointer _ => true
340+
| .tuple ts => ts.attach.any fun ⟨t, _⟩ => hasPointer t
341+
| .array t _ => hasPointer t
342+
| .app _ args => args.attach.any fun ⟨t, _⟩ => hasPointer t
343+
| .function ins out =>
344+
hasPointer out || ins.attach.any fun ⟨t, _⟩ => hasPointer t
345+
termination_by t => sizeOf t
346+
decreasing_by
347+
all_goals first
348+
| decreasing_tactic
349+
| (have := Array.sizeOf_lt_of_mem ‹_ ∈ _›; grind)
350+
| (have := List.sizeOf_lt_of_mem ‹_ ∈ _›; grind)
351+
335352
end Typ
336353

337354
instance : BEq Typ := ⟨Typ.beq⟩
@@ -436,14 +453,55 @@ structure TypeAlias where
436453

437454
namespace Source
438455

456+
/-- `true` iff none of `inputs` nor `output` contain a `.pointer` subterm.
457+
Used to enforce that public entry functions expose no pointer-typed values. -/
458+
def sigPointerFree (inputs : List (Local × Typ)) (output : Typ) : Bool :=
459+
!output.hasPointer && inputs.all (fun ⟨_, t⟩ => !t.hasPointer)
460+
439461
structure Function where
440462
name : Global
441463
params : List String
442464
inputs : List (Local × Typ)
443465
output : Typ
444466
body : Term
445467
entry : Bool
446-
deriving Repr, Inhabited
468+
/-- Polymorphic public entry points are forbidden by construction:
469+
either the function is monomorphic (`params = []`) or not public
470+
(`entry = false`). -/
471+
entryMonomorphic : params = [] ∨ entry = false := by
472+
first | exact Or.inl rfl | exact Or.inr rfl
473+
/-- Public entry points cannot expose pointer-typed values: either the
474+
signature is pointer-free or the function is not public (`entry = false`). -/
475+
entryPointerFree : sigPointerFree inputs output = true ∨ entry = false := by
476+
first | exact Or.inl rfl | exact Or.inr rfl
477+
deriving Repr
478+
479+
instance : Inhabited Function where
480+
default :=
481+
{ name := default, params := [], inputs := default, output := default,
482+
body := default, entry := default,
483+
entryMonomorphic := Or.inl rfl,
484+
entryPointerFree := Or.inr rfl }
485+
486+
/-- Smart constructor for non-entry monomorphic functions. -/
487+
def Function.monoNonEntry (name : Global) (inputs : List (Local × Typ))
488+
(output : Typ) (body : Term) : Function :=
489+
{ name, params := [], inputs, output, body, entry := false,
490+
entryMonomorphic := Or.inl rfl, entryPointerFree := Or.inr rfl }
491+
492+
/-- Smart constructor for public entry functions. Requires a proof that the
493+
signature contains no pointer types. -/
494+
def Function.monoEntry (name : Global) (inputs : List (Local × Typ))
495+
(output : Typ) (body : Term)
496+
(h : sigPointerFree inputs output = true) : Function :=
497+
{ name, params := [], inputs, output, body, entry := true,
498+
entryMonomorphic := Or.inl rfl, entryPointerFree := Or.inl h }
499+
500+
/-- Smart constructor for polymorphic functions (`entry = false` forced). -/
501+
def Function.poly (name : Global) (params : List String) (inputs : List (Local × Typ))
502+
(output : Typ) (body : Term) : Function :=
503+
{ name, params, inputs, output, body, entry := false,
504+
entryMonomorphic := Or.inr rfl, entryPointerFree := Or.inr rfl }
447505

448506
structure Toplevel where
449507
dataTypes : Array DataType

0 commit comments

Comments
 (0)