Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 13 additions & 18 deletions Strata/DL/Lambda/LExprTypeEnv.lean
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,6 @@ deriving Inhabited
def LDatatype.toKnownType (d: LDatatype IDMeta) : KnownType :=
{ name := d.name, metadata := d.typeArgs.length}

def TypeFactory.toKnownTypes (t: @TypeFactory IDMeta) : KnownTypes :=
makeKnownTypes (t.foldr (fun t l => t.toKnownType :: l) [])

/--
A type environment `TEnv` contains
- genEnv: `TGenEnv` to track the generator state as well as the typing context
Expand Down Expand Up @@ -333,28 +330,26 @@ def LContext.addFactoryFunctions (C : LContext T) (fact : @Factory T) : LContext
{ C with functions := C.functions.append fact }

/--
Add a datatype `d` to an `LContext` `C`.
This adds `d` to `C.datatypes`, adds the derived functions
(e.g. eliminators, testers) to `C.functions`, and adds `d` to
`C.knownTypes`. It performs error checking for name clashes.
Add a mutual block of datatypes `block` to an `LContext` `C`.
This adds all types to `C.datatypes` and `C.knownTypes`,
adds the derived functions (e.g. eliminators, testers),
and performs error checking for name clashes.
-/
def LContext.addDatatype [Inhabited T.IDMeta] [Inhabited T.Metadata] (C: LContext T) (d: LDatatype T.IDMeta) : Except Format (LContext T) := do
-- Ensure not in known types
if C.knownTypes.containsName d.name then
.error f!"Cannot name datatype same as known type!\n\
{d}\n\
KnownTypes' names:\n\
{C.knownTypes.keywords}"
let ds ← C.datatypes.addDatatype d
def LContext.addMutualBlock [Inhabited T.IDMeta] [Inhabited T.Metadata] [ToFormat T.IDMeta] (C: LContext T) (block: MutualDatatype T.IDMeta) : Except Format (LContext T) := do
-- Check for name clashes with known types
for d in block do
if C.knownTypes.containsName d.name then
throw f!"Cannot name datatype same as known type!\n{d}\nKnownTypes' names:\n{C.knownTypes.keywords}"
let ds ← C.datatypes.addMutualBlock block C.knownTypes.keywords
-- Add factory functions, checking for name clashes
let f ← d.genFactory
let f ← genBlockFactory block
let fs ← C.functions.addFactory f
-- Add datatype names to knownTypes
let ks ← C.knownTypes.add d.toKnownType
let ks ← block.foldlM (fun ks d => ks.add d.toKnownType) C.knownTypes
.ok {C with datatypes := ds, functions := fs, knownTypes := ks}

def LContext.addTypeFactory [Inhabited T.IDMeta] [Inhabited T.Metadata] (C: LContext T) (f: @TypeFactory T.IDMeta) : Except Format (LContext T) :=
Array.foldlM (fun C d => C.addDatatype d) C f
f.foldlM (fun C block => C.addMutualBlock block) C

/--
Replace the global substitution in `T.state.subst` with `S`.
Expand Down
375 changes: 263 additions & 112 deletions Strata/DL/Lambda/TypeFactory.lean

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions Strata/DL/SMT/Solver.lean
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,20 @@ def declareDatatype (id : String) (params : List String) (constructors : List St
then emitln s!"(declare-datatype {id} ({cInline}))"
else emitln s!"(declare-datatype {id} (par ({pInline}) ({cInline})))"

/-- Declare multiple mutually recursive datatypes. Each element is (name, params, constructors). -/
def declareDatatypes (dts : List (String × List String × List String)) : SolverM Unit := do
if dts.isEmpty then return
let sortDecls := dts.map fun (name, params, _) => s!"({name} {params.length})"
let sortDeclStr := String.intercalate " " sortDecls
let bodies := dts.map fun (_, params, constrs) =>
let cInline := String.intercalate " " constrs
if params.isEmpty then s!"({cInline})"
else
let pInline := String.intercalate " " params
s!"(par ({pInline}) ({cInline}))"
let bodyStr := String.intercalate "\n " bodies
emitln s!"(declare-datatypes ({sortDeclStr})\n ({bodyStr}))"

private def readlnD (dflt : String) : SolverM String := do
match (← read).smtLibOutput with
| .some stdout => stdout.getLine
Expand Down
11 changes: 7 additions & 4 deletions Strata/Languages/Core/DDMTransform/Translate.lean
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,13 @@ partial def translateLMonoTy (bindings : TransBindings) (arg : Arg) :
| .type (.syn syn) _md =>
let ty := syn.toLHSLMonoTy
pure ty
| .type (.data ldatatype) =>
| .type (.data (ldatatype :: _)) _md =>
-- Datatype Declaration
-- TODO: Handle mutual blocks, need to find the specific datatype by name
let args := ldatatype.typeArgs.map LMonoTy.ftvar
pure (.tcons ldatatype.name args)
| .type (.data []) _md =>
TransM.error "Empty mutual datatype block"
| _ =>
TransM.error
s!"translateLMonoTy not yet implemented for this declaration: \
Expand Down Expand Up @@ -1347,7 +1350,7 @@ def translateDatatype (p : Program) (bindings : TransBindings) (op : Operation)
typeArgs := typeArgs
constrs := [{ name := datatypeName, args := [], testerName := "" }]
constrs_ne := by simp }
let placeholderDecl := Core.Decl.type (.data placeholderLDatatype)
let placeholderDecl := Core.Decl.type (.data [placeholderLDatatype])
let bindingsWithPlaceholder := { bindings with freeVars := bindings.freeVars.push placeholderDecl }

-- Extract constructor information (possibly recursive)
Expand Down Expand Up @@ -1376,14 +1379,14 @@ def translateDatatype (p : Program) (bindings : TransBindings) (op : Operation)

-- Generate factory from LDatatype and convert to Core.Decl
-- (used only for bindings.freeVars, not for allDecls)
let factory ← match ldatatype.genFactory (T := CoreLParams) with
let factory ← match genBlockFactory [ldatatype] (T := CoreLParams) with
| .ok f => pure f
| .error e => TransM.error s!"Failed to generate datatype factory: {e}"
let funcDecls : List Core.Decl := factory.toList.map fun func =>
Core.Decl.func func

-- Only includes typeDecl, factory functions generated later
let typeDecl := Core.Decl.type (.data ldatatype)
let typeDecl := Core.Decl.type (.data [ldatatype])
let allDecls := [typeDecl]

/-
Expand Down
9 changes: 6 additions & 3 deletions Strata/Languages/Core/Env.lean
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,13 @@ def Env.merge (cond : Expression.Expr) (E1 E2 : Env) : Env :=
else
Env.performMerge cond E1 E2 (by simp_all) (by simp_all)

def Env.addDatatypes (E: Env) (datatypes: List (Lambda.LDatatype Visibility)) : Except Format Env := do
let f ← Lambda.TypeFactory.genFactory (T:=CoreLParams) (datatypes.toArray)
def Env.addMutualDatatype (E: Env) (block: Lambda.MutualDatatype Visibility) : Except Format Env := do
let f ← Lambda.genBlockFactory (T:=CoreLParams) block
let env ← E.addFactory f
return { env with datatypes := datatypes.toArray }
return { env with datatypes := E.datatypes.push block }

def Env.addDatatypes (E: Env) (blocks: List (Lambda.MutualDatatype Visibility)) : Except Format Env :=
blocks.foldlM Env.addMutualDatatype E

end Core

Expand Down
8 changes: 7 additions & 1 deletion Strata/Languages/Core/Program.lean
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ def Decl.name (d : Decl) : Expression.Ident :=
| .proc p _ => p.header.name
| .func f _ => f.name

/-- Get all names from a declaration. For mutual datatypes, returns all datatype names. -/
def Decl.names (d : Decl) : List Expression.Ident :=
match d with
| .type t _ => t.names
| _ => [d.name]

def Decl.getVar? (d : Decl) :
Option (Expression.Ident × Expression.Ty × Expression.Expr) :=
match d with
Expand Down Expand Up @@ -207,7 +213,7 @@ def Program.getInit? (P: Program) (x : Expression.Ident) : Option Expression.Exp

def Program.getNames (P: Program) : List Expression.Ident :=
go P.decls
where go decls := decls.map Decl.name
where go decls := decls.flatMap Decl.names

def Program.Type.find? (P : Program) (x : Expression.Ident) : Option TypeDecl :=
match P.find? .type x with
Expand Down
12 changes: 7 additions & 5 deletions Strata/Languages/Core/ProgramType.lean
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ def typeCheck (C: Core.Expression.TyContext) (Env : Core.Expression.TyEnv) (prog
| decl :: drest => do
let sourceLoc := Imperative.MetaData.formatFileRangeD decl.metadata (includeEnd? := true)
let errorWithSourceLoc := fun e => if sourceLoc.isEmpty then e else f!"{sourceLoc} {e}"
let C := {C with idents := (← C.idents.addWithError decl.name
f!"{sourceLoc} Error in {decl.kind} {decl.name}: \
a declaration of this name already exists.")}
-- Add all names from the declaration (multiple for mutual datatypes)
let C ← decl.names.foldlM (fun C name => do
let idents ← C.idents.addWithError name
f!"{sourceLoc} Error in {decl.kind} {name}: a declaration of this name already exists."
pure {C with idents}) C
let (decl', C, Env) ←
match decl with

Expand All @@ -61,8 +63,8 @@ def typeCheck (C: Core.Expression.TyContext) (Env : Core.Expression.TyEnv) (prog
| .syn ts =>
let Env ← TEnv.addTypeAlias { typeArgs := ts.typeArgs, name := ts.name, type := ts.type } C Env
.ok (.type td, C, Env)
| .data d =>
let C ← C.addDatatype d
| .data block =>
let C ← C.addMutualBlock block
.ok (.type td, C, Env)
catch e =>
.error (errorWithSourceLoc e)
Expand Down
10 changes: 10 additions & 0 deletions Strata/Languages/Core/ProgramWF.lean
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,22 @@ theorem addKnownTypeWithErrorIdents {C: Expression.TyContext}: C.addKnownTypeWit
case error => intros _; contradiction
case ok k'=> simp[Except.bind]; intros T'; subst T'; rfl

/-
theorem addDatatypeIdents {C: Expression.TyContext}: C.addDatatype d = .ok C' → C.idents = C'.idents := by
unfold LContext.addDatatype;
simp only[bind, Except.bind, pure, Except.pure]; intros Hok
repeat (split at Hok <;> try contradiction)
cases Hok <;> rfl
-/

/--
If a program typechecks successfully, then every identifier in the list of
program decls is not in the original `LContext`
-/
theorem Program.typeCheckFunctionDisjoint : Program.typeCheck.go p C T decls acc = .ok (d', T') → (∀ x, x ∈ Program.getNames.go decls → ¬ C.idents.contains x) := by
-- TODO: This proof needs to be updated to handle mutual datatypes (multiple names per decl)
sorry
/-
induction decls generalizing acc p d' T' T C with
| nil => simp[Program.getNames.go]
| cons r rs IH =>
Expand Down Expand Up @@ -329,12 +334,16 @@ theorem Program.typeCheckFunctionDisjoint : Program.typeCheck.go p C T decls acc
rename_i hmatch2; split at hmatch2 <;> try grind
simp only [LContext.addFactoryFunction] at hmatch2; grind
done
-/

/--
If a program typechecks succesfully, all identifiers defined in the program are
unique.
-/
theorem Program.typeCheckFunctionNoDup : Program.typeCheck.go p C T decls acc = .ok (d', T') → (Program.getNames.go decls).Nodup := by
-- TODO: This proof needs to be updated to handle mutual datatypes (multiple names per decl)
sorry
/-
induction decls generalizing acc p C T with
| nil => simp[Program.getNames.go]
| cons r rs IH =>
Expand Down Expand Up @@ -376,6 +385,7 @@ theorem Program.typeCheckFunctionNoDup : Program.typeCheck.go p C T decls acc =
rename_i hmatch2; split at hmatch2 <;> try grind
simp only [LContext.addFactoryFunction] at hmatch2; grind
done
-/

/--
The main lemma stating that a program 'p' that passes type checking is well formed
Expand Down
Loading
Loading