Zulip Chat Archive

Stream: Program verification

Topic: Cases on operational semantics relation


Siddharth Priya (Jan 23 2025 at 16:29):

Apologies for the long listing.
I have two __equivalent__ definitions Eval and Eval2 of a small step operational semantics for a control flow graph based programming language. Eval uses a structure andEval2 uses two Words for bb,stmt directly rather than a structure.

structure ProgCount where
  bb: Word
  stmt: Word
deriving Inhabited, Repr

inductive Eval : ProgCount × Prog × Env × Mem × AccessPerms 
  ProgCount × Prog × Env × Mem × AccessPerms 
    Prop
  | AssgnRel :  (pc: ProgCount) (newpc: ProgCount) (lplace : List Word) (rexpr : RExpr) (env : Env) (mem : Mem)
    (ap : AccessPerms) (values : List MemValue) (ty : TyVal) (newAp : AccessPerms)
    (newMem : Mem) (newEnv : Env) (prog: Prog) (stmt_list: List Stmt),
    prog.find? pc.bb = some stmt_list 
    (h : pc.stmt < stmt_list.length) 
    stmt_list.get pc.stmt, h = Stmt.Assgn lplace rexpr 
    RExprToValFn rexpr env mem ap = RhsResult.Ok values ty newAp newMem 
    (match lplace with
    | [newplace] =>
      let (newAddr, allocatedMem) := allocate newMem (typeSize ty)
      let updatedMem := WriteWordSeq allocatedMem newAddr values
      let (tag, newAp) := freshTag newAp
      let updatedEnv := env.insert newplace (newAddr, ty, tag)
      LhsResult.Ok updatedEnv newAp updatedMem
    | _ =>
      match getPlaceAddr lplace env with
      | none => LhsResult.Err s!"lhs Place {lplace} not found in environment."
      | some addr =>
        let updatedMem := WriteWordSeq newMem addr values
        LhsResult.Ok env newAp updatedMem) = LhsResult.Ok newEnv newAp newMem 
        newpc = {pc with stmt := pc.stmt + 1} 
    Eval (pc, prog, env, mem, ap) (newpc, prog, newEnv, newMem, newAp)
  | HaltRel :  (pc: ProgCount) (prog: Prog) (env : Env) (mem : Mem) (ap : AccessPerms) (stmt_list: List Stmt),
    prog.find? pc.bb = some stmt_list 
    (h: pc.stmt < stmt_list.length) 
    stmt_list.get pc.stmt, h = Stmt.Halt 
    Eval (pc, prog, env, mem, ap) (pc, prog, env, mem, ap)

When I want to prove the following theorem, then on the reverse implication, LEAN is not happy using case only with HaltRel

theorem step_pc_stays_same_iff_halt
    {pc : ProgCount} {prog : Prog} {env : Env} {mem : Mem} {ap : AccessPerms}
    {stmt_list : List Stmt}
    (h_bb : prog.find? pc.bb = some stmt_list)
    (h_stmt: pc.stmt < stmt_list.length):
      (stmt_list.get pc.stmt, h_stmt = Stmt.Halt) 
    Eval (pc, prog, env, mem, ap) (pc, prog, env, mem, ap) := by
  apply Iff.intro
  { intros h_stmt_eq
    apply Eval.HaltRel
    exact h_bb
    exact h_stmt_eq
  }
  {
    intros h_eval
    cases h_eval
    case HaltRel s1 s2 s3 s4 =>
     -- Unify stmt_list and s1
      have : stmt_list = s1 := by
        rw [s4] at h_bb
        injection h_bb with h_eq
        symm
        exact h_eq
      subst this
      exact s3
  }

It says

unsolved goals
case mpr.AssgnRel
pc : ProgCount
prog : Prog
env : Env
mem : Mem
ap : AccessPerms
stmt_list : List Stmt
h_bb : Lean.AssocList.find? pc.bb prog = some stmt_list
h_stmt : pc.stmt < stmt_list.length
lplace : List Word
rexpr : RExpr
values : List MemValue
ty : TyVal
stmt_list : List Stmt
h : pc.stmt < stmt_list✝.length
a✝⁴ : stmt_list✝.get pc.stmt, h✝⟩ = Assgn lplace rexpr
a✝³ : Lean.AssocList.find? pc.bb prog = some stmt_list
a✝² : pc = { bb := pc.bb, stmt := pc.stmt + 1 }
a✝¹ : RExprToValFn rexpr env mem ap = RhsResult.Ok values ty ap mem
a :
  (match lplace with
    | [newplace] =>
      match allocate mem (typeSize ty) with
      | (newAddr, allocatedMem) =>
        let updatedMem := WriteWordSeq allocatedMem newAddr values✝;
        match freshTag ap with
        | (tag, newAp) =>
          let updatedEnv := Lean.AssocList.insert env newplace (newAddr, ty, tag);
          LhsResult.Ok updatedEnv newAp updatedMem
    | x =>
      match getPlaceAddr lplace env with
      | none =>
        LhsResult.Err
          (ToString.toString "lhs Place " ++ ToString.toString lplace ++ ToString.toString " not found in environment.")
      | some addr =>
        let updatedMem := WriteWordSeq mem addr values✝;
        LhsResult.Ok env ap updatedMem) =
    LhsResult.Ok env ap mem
 stmt_list.get pc.stmt, h_stmt = Halt

However if I tweak the relation such that the Program Counter is not a structure but individual Words (Nat), then LEAN is convinced that only one case (HaltRel) is enough on the reverse implication. Why?

abbrev PC := Word
abbrev BB := Word

inductive Eval2 : BB × PC  × Prog × Env × Mem × AccessPerms 
  BB × PC × Prog × Env × Mem × AccessPerms 
    Prop
  | AssgnRel :  (bb: BB)  (pc: PC) (newpc: ProgCount) (lplace : List Word) (rexpr : RExpr) (env : Env) (mem : Mem)
    (ap : AccessPerms) (values : List MemValue) (ty : TyVal) (newAp : AccessPerms)
    (newMem : Mem) (newEnv : Env) (prog: Prog) (stmt_list: List Stmt),
    prog.find? bb = some stmt_list 
    (h : pc < stmt_list.length) 
    stmt_list.get pc, h = Stmt.Assgn lplace rexpr 
    RExprToValFn rexpr env mem ap = RhsResult.Ok values ty newAp newMem 
    (match lplace with
    | [newplace] =>
      let (newAddr, allocatedMem) := allocate newMem (typeSize ty)
      let updatedMem := WriteWordSeq allocatedMem newAddr values
      let (tag, newAp) := freshTag newAp
      let updatedEnv := env.insert newplace (newAddr, ty, tag)
      LhsResult.Ok updatedEnv newAp updatedMem
    | _ =>
      match getPlaceAddr lplace env with
      | none => LhsResult.Err s!"lhs Place {lplace} not found in environment."
      | some addr =>
        let updatedMem := WriteWordSeq newMem addr values
        LhsResult.Ok env newAp updatedMem) = LhsResult.Ok newEnv newAp newMem 
    Eval2 (bb, pc, prog, env, mem, ap) (bb, pc + 1, prog, newEnv, newMem, newAp)
  | HaltRel :  (bb: BB) (pc: PC) (prog: Prog) (env : Env) (mem : Mem) (ap : AccessPerms) (stmt_list: List Stmt),
    prog.find? bb = some stmt_list 
    (h: pc < stmt_list.length) 
    stmt_list.get pc, h = Stmt.Halt 
    Eval2 (bb, pc, prog, env, mem, ap) (bb, pc, prog, env, mem, ap)


theorem step_pc_stays_same_iff_halt2
    {bb: BB} {pc : PC} {prog : Prog} {env : Env} {mem : Mem} {ap : AccessPerms}
    {stmt_list : List Stmt}
    (h_bb : prog.find? bb = some stmt_list)
    (h_stmt: pc < stmt_list.length):
      (stmt_list.get pc, h_stmt = Stmt.Halt) 
    Eval2 (bb, pc, prog, env, mem, ap) (bb, pc, prog, env, mem, ap) := by
  apply Iff.intro
  { intros h_stmt_eq
    apply Eval2.HaltRel
    exact h_bb
    exact h_stmt_eq
  }
  {
    intros h
    cases h
    case HaltRel s1 s2 s3 s4 =>
     -- Unify stmt_list and s1
      have : stmt_list = s1 := by
        rw [s4] at h_bb
        injection h_bb with h_eq
        symm
        exact h_eq
      subst this
      exact s3
}

Last updated: May 02 2025 at 03:31 UTC