Theory SemiType

Up to index of Isabelle/HOL/JinjaThreads

theory SemiType
imports WellForm Semilattices

(*  Title:      JinjaThreads/BV/SemiType.thy
    Author:     Tobias Nipkow, Gerwin Klein, Andreas Lochbihler
*)

header {* 
  \chapter{Bytecode verifier}
  \isaheader{The Jinja Type System as a Semilattice} 
*}

theory SemiType
imports "../Common/WellForm" "../../Jinja/DFA/Semilattices"
begin

inductive_set
  widen1 :: "'a prog => (ty × ty) set"
  and widen1_syntax :: "'a prog => ty => ty => bool" ("_ \<turnstile> _ <1 _" [71,71,71] 70)
  for P :: "'a prog"
where
  "P \<turnstile> C <1 D ≡ (C, D) ∈ widen1 P"

| widen1_Array_Object:
  "(* is_class P Object ==> *) P \<turnstile> Array (Class Object) <1 Class Object"

| widen1_Array_Integer:
  "P \<turnstile> Array Integer <1 Class Object"

| widen1_Array_Boolean:
  "P \<turnstile> Array Boolean <1 Class Object"

| widen1_Array_Void:
  "P \<turnstile> Array Void <1 Class Object"

| widen1_Class: 
  "P \<turnstile> C \<prec>1 D ==> P \<turnstile> Class C <1 Class D"

| widen1_Array_Array:
  "[| P \<turnstile> T <1 U; ¬ is_NT_Array T |] ==> P \<turnstile> Array T <1 Array U"

| widen1_NT_Object:
  "is_NT_Array T ==> P \<turnstile> Array T <1 Class Object"

abbreviation widen1_trancl :: "'a prog => ty => ty => bool" ("_ \<turnstile> _ <+ _" [71,71,71] 70) where
  "P \<turnstile> T <+ U ≡ (T, U) ∈ trancl (widen1 P)"

lemma widen1_Integer [iff]:
  "¬ P \<turnstile> Integer <1 T"
  by(auto elim: widen1.cases)

lemma widen1_Boolean [iff]:
  "¬ P \<turnstile> Boolean <1 T"
  by(auto elim: widen1.cases)

lemma widen1_Void [iff]:
  "¬ P \<turnstile> Void <1 T"
  by(auto elim: widen1.cases)

lemma widen1_Class_Object [iff]:
  "¬ P \<turnstile> Class Object <1 T"
  by(auto elim: widen1.cases)

lemma widen1_NT [simp]: "¬ P \<turnstile> NT <1 U"
by(auto elim: widen1.cases)

lemma is_type_widen1: 
  assumes icO: "is_class P Object"
  shows "P \<turnstile> T <1 U ==> is_type P T"
apply(induct rule: widen1.induct)
apply(auto intro: subcls_is_class NT_Array_is_type icO)
done


lemma widen1_NT_Array:
  assumes NT: "is_NT_Array T"
  shows "P \<turnstile> T⌊⌉ <1 U <-> U = Class Object"
proof(rule iffI)
  assume "P \<turnstile> T⌊⌉ <1 U"
  moreover
  { fix T'
    have "[| P \<turnstile> T' <1 U; T' = T⌊⌉; is_NT_Array T' |] ==> U = Class Object"
      by(induct arbitrary: T rule: widen1.induct, auto) }
  ultimately show "U = Class Object" using NT by auto
next
  assume "U = Class Object"
  with NT show "P \<turnstile> T⌊⌉ <1 U"
    by(auto intro: widen1_NT_Object)
qed

lemma widen1_is_type:
  assumes wfP: "wf_prog wfmd P"
  shows "(A, B) ∈ widen1 P ==> is_type P B"
proof(induct rule: widen1.induct)
  case (widen1_Class C D)
  note CD = `P \<turnstile> C \<prec>1 D`
  hence "is_class P C"
    by(auto intro: subcls_is_class)
  with CD have "is_class P D"
    by(auto intro: converse_subcls_is_class[OF wfP])
  thus ?case by simp
qed(insert wfP, auto intro: NT_Array_is_type)

lemma widen1_trancl_is_type:
  assumes wfP: "wf_prog wfmd P"
  shows "(A, B) ∈ (widen1 P)^+ ==> is_type P B"
apply(induct rule: trancl_induct)
apply(auto intro: widen1_is_type[OF wfP])
done

lemma single_valued_widen1:
  assumes wf: "wf_prog wf_md P"
  shows "single_valued (widen1 P)"
proof(rule single_valuedI)
  { fix x y z
    have "[| P \<turnstile> x <1 y; P \<turnstile> x <1 z |] ==> y = z"
    proof(induct arbitrary: z rule: widen1.induct)
      case widen1_Class
      with single_valued_subcls1[OF wf] show ?case
        by(auto dest: single_valuedD elim!: widen1.cases)
    next
      case (widen1_Array_Array T U z)
      from `P \<turnstile> T⌊⌉ <1 z` `P \<turnstile> T <1 U` `¬ is_NT_Array T`
      obtain z'
        where z': "z = z'⌊⌉"
        and Tz': "P \<turnstile> T <1 z'"
        by(auto elim: widen1.cases)
      with `!!z. P \<turnstile> T <1 z ==> U = z` have "U = z'" by blast
      with z' show ?case by simp
    qed(auto elim: widen1.cases) }
  thus "∀x y. P \<turnstile> x <1 y --> (∀z. P \<turnstile> x <1 z --> y = z)" by blast
qed

function class_numbering :: "'a prog => cname => nat" where
  "class_numbering P C =
   (if acyclicP (subcls1 P) ∧ is_class P C ∧ C ≠ Object
    then Suc (class_numbering P (fst (the (class P C))))
    else 0)"
by(pat_completeness, auto)
termination
proof(relation "{((P, C), (P', C')). P = P' ∧ acyclicP (subcls1 P) ∧ P \<turnstile> C' \<prec>1 C}")
  show "wf {((P, C), P', C'). P = P' ∧ acyclicP (subcls1 P) ∧ P \<turnstile> C' \<prec>1 C}"
  proof(rule wfUNIVI)
    fix Q x
    assume "∀x. (∀y. (y, x) ∈ {((P, C), P', C'). P = P' ∧ acyclicP (subcls1 P) ∧ P \<turnstile> C' \<prec>1 C} --> Q y) --> Q x"
    hence wf: "!!x. (!!y. (y, x) ∈ {((P, C), P', C'). P = P' ∧ acyclicP (subcls1 P) ∧ P \<turnstile> C' \<prec>1 C} ==> Q y) ==> Q x" by blast
    obtain P C where PC: "(x :: 'c prog × cname) = (P, C)" by(cases x, auto)
    show "Q x"
    proof(cases "acyclicP (subcls1 P)")
      case False
      with PC show "Q x"
        by(auto intro: wf)
    next
      case True
      from True
      have wf'': "!!x. (!!y. (subcls1 P)¯¯ y x ==> Q (P, y)) ==> Q (P, x)"
        by(auto intro: wf)
      show ?thesis
      proof(unfold PC, rule wf_induct[where P="λC. Q (P, C)"])
        from True finite_subcls1'[of P] show "wf {(D, C). P \<turnstile> C \<prec>1 D}"
          apply -
          apply(erule finite_acyclic_wf)
          apply(subst acyclic_converse[symmetric])
          by(simp add: converse_def del: acyclic_converse)
      next
        fix x
        assume "∀y. (y, x) ∈ {(D, C). P \<turnstile> C \<prec>1 D} --> Q (P, y)"
        thus "Q (P, x)"
          by(auto intro: wf'')
      qed
    qed
  qed
next
  fix P C
  assume "acyclicP (subcls1 P) ∧ is_class P C ∧ C ≠ Object"
  then obtain ac: "acyclicP (subcls1 P)"
    and ic: "is_class P C"
    and CObj: "C ≠ Object" by blast
  from ic CObj have "P \<turnstile> C \<prec>1 fst (the (class P C))"
    by(auto simp add: is_class_def intro: subcls1I)
  with ac show "((P, fst (the (class P C))), P, C) ∈ {((P, C), P', C'). P = P' ∧ acyclicP (subcls1 P) ∧ P \<turnstile> C' \<prec>1 C}"
    by(simp)
qed

fun wf_measure_widen1 :: "'a prog => ty => nat" where
  "wf_measure_widen1 P (Class C) = class_numbering P C"
| "wf_measure_widen1 P (Array T) = length P + wf_measure_widen1 P T"
| "wf_measure_widen1 P T = 0"

lemma wf_converse_widen1:
  assumes wfP: "wf_prog wfmc P"
  shows "wf ((widen1 P)^-1)"
proof(rule wf_subset)
  show "wf (measure (wf_measure_widen1 P))"
    by auto
next
  from wfP have lengthP: "length P > 0"
    by(auto simp add: wf_prog_def wf_syscls_def)
  { fix x y
    have "P \<turnstile> x <1 y ==> wf_measure_widen1 P y < wf_measure_widen1 P x"
    proof(induct rule: widen1.induct)
      case (widen1_Class C D)
      note PCD = `P \<turnstile> C \<prec>1 D`
      from wfP have "acyclicP (subcls1 P)"
        by(rule acyclic_subcls1)
      moreover from PCD have "is_class P C" "C ≠ Object"
        by(auto elim: subcls1.cases simp: is_class_def)
      moreover from PCD obtain rest
        where "class P C = ⌊(D, rest)⌋"
        by(auto elim!: subcls1.cases)
      ultimately show ?case 
        by(simp del: class_numbering.simps add: class_numbering.simps[where C=C])
    qed(insert lengthP, simp_all) }
  thus "(widen1 P)¯ ⊆ measure (wf_measure_widen1 P)" by auto
qed

fun super :: "'a prog => ty => ty"
where
  "super P (Array Integer) = Class Object"
| "super P (Array Boolean) = Class Object"
| "super P (Array Void) = Class Object"
| "super P (Array (Class C)) = (if C = Object then Class Object else Array (super P (Class C)))"
| "super P (Array NT) = Class Object"
| "super P (Array (Array T)) = (if is_NT_Array T then Class Object else Array (super P (Array T)))"
| "super P (Class C) = Class (fst (the (class P C)))"

lemma superI:
  "P \<turnstile> T <1 U ==> super P T = U"
proof(induct rule: widen1.induct)
  case (widen1_Array_Array T U)
  thus ?case by(cases T, auto elim: widen1.cases)
next
  case (widen1_NT_Object T)
  thus ?case by(cases T, auto)
qed(auto dest: subcls1D)

lemma super_widen1:
  assumes icO: "is_class P Object"
  shows "P \<turnstile> T <1 U <-> is_type P T ∧ (case T of Class C  => (C ≠ Object ∧ U = super P T) 
                                              | Array T' => U = super P T 
                                              | _        => False)"
proof(induct T arbitrary: U)
  case (Class C' U')
  have "P \<turnstile> Class C' <1 U' <-> is_class P C' ∧ C' ≠ Object ∧ U' = super P (Class C')"
  proof(rule iffI)
    assume wd: "P \<turnstile> Class C' <1 U'"
    hence "is_type P (Class C')"
      by(auto elim!: widen1.cases intro: subcls_is_class)
    moreover from wd have "C' ≠ Object" by(auto)
    moreover from wd have "super P (Class C') = U'"
      by(rule superI)
    ultimately show "is_class P C' ∧ C' ≠ Object ∧ U' = super P (Class C')"
      by simp
  next
    assume "is_class P C' ∧ C' ≠ Object ∧ U' = super P (Class C')"
    then obtain "is_class P C'" "C' ≠ Object" "U' = super P (Class C')" by blast
    from `U' = super P (Class C')` obtain D where "U' = Class D" by(auto)
    moreover with `is_class P C'` `U' = super P (Class C')` `C' ≠ Object`
    have "P \<turnstile> C' \<prec>1 D" by(auto simp add: is_class_def intro!: subcls1.intros)
    hence "P \<turnstile> Class C' <1 Class D" by(rule widen1_Class)
    ultimately show "P \<turnstile> Class C' <1 U'" by simp
  qed
  thus ?case by simp
next
  case (Array T' U')
  have "P \<turnstile> T'⌊⌉ <1 U' = (is_type P T' ∧ U' = super P (T'⌊⌉))"
  proof(rule iffI)
    assume wd: "P \<turnstile> T'⌊⌉ <1 U'"
    hence "is_type P T'" using icO
      by(auto dest: is_type_widen1)
    moreover from wd have "super P (T'⌊⌉) = U'" by(rule superI)
    ultimately show "is_type P T' ∧ U' = super P (T'⌊⌉)" by simp
  next
    assume "is_type P T' ∧ U' = super P (T'⌊⌉)"
    then obtain "is_type P T'"
      and U': "U' = super P (T'⌊⌉)" by blast
    from U' show "P \<turnstile> T'⌊⌉ <1 U'"
    proof(cases T')
      case (Class D)
      show ?thesis
      proof(cases "D = Object")
        case True
        with Class U' icO
        show ?thesis by(auto intro: widen1_Array_Object)
      next
        case False
        with Class U' obtain D' where "U' = (Class D')⌊⌉" by auto
        with U' Class False `is_type P T'` have "P \<turnstile> D \<prec>1 D'"
          by(auto simp add: is_class_def intro: subcls1.intros)
        hence "P \<turnstile> Class D <1 Class D'" by(rule widen1_Class)
        with `U' = (Class D')⌊⌉` Class show ?thesis
          by(auto intro: widen1_Array_Array)
      qed
    next
      case (Array V)
      show ?thesis
      proof(cases "is_NT_Array V")
        case True
        with Array U' have "U' = Class Object" by(simp)
        with True Array show ?thesis by(auto intro: widen1_NT_Object)
      next
        case False
        with Array U' obtain V' where "U' = V'⌊⌉" by(auto)
        with Array `is_type P T'` U' False 
        have "P \<turnstile> T' <1 V'"
          unfolding `!!U. P \<turnstile> T' <1 U = (is_type P T' ∧ (case T' of Class C => C ≠ Object ∧ U = super P T' | T⌊⌉ => U = super P T' | _ => False))`
          by(simp)
        with `U' = V'⌊⌉` False Array show ?thesis 
          by(auto intro: widen1_Array_Array)
      qed
    qed(auto intro: widen1.intros)
  qed    
  thus ?case by(simp)
qed(simp_all)


definition sup :: "'c prog => ty => ty => ty err" where
  "sup P T U ≡
   if is_refT T ∧ is_refT U
   then OK (if U = NT then T
            else if T = NT then U
            else exec_lub (widen1 P) (super P) T U)
   else if (T = U) then OK T else Err"

constdefs
  esl :: "'c prog => ty esl"
  "esl P ≡ (is_type P, widen P, sup P)"



lemma order_widen [intro,simp]: 
  "wf_prog m P ==> order (widen P)"
unfolding Semilat.order_def lesub_def
by (auto intro: widen_trans widen_antisym)



lemma subcls1_trancl_widen1_trancl:
  "(subcls1 P)^++ C D ==> (Class C, Class D) ∈ (widen1 P)^+"
proof(induct rule: tranclp_induct[consumes 1, case_names base step])
  case base thus ?case
    by -(rule r_into_trancl, rule widen1.intros)
next
  case (step D E)
  from `P \<turnstile> D \<prec>1 E` have "(Class D, Class E) ∈ widen1 P"
    by(rule widen1.intros)
  with `(Class C, Class D) ∈ (widen1 P)+` show ?case
    by(rule trancl_into_trancl)
qed

lemma subcls_into_widen1_rtrancl:
  "P \<turnstile> C \<preceq>* D ==> (Class C, Class D) ∈ (widen1 P)*"
by(induct rule: rtranclp.induct)(auto intro: rtrancl_refl widen1_Class elim: rtrancl_into_rtrancl)

lemma not_widen1_NT_Array:
  "[| P \<turnstile> U <1 T; is_NT_Array T |] ==> False"
by(induct rule: widen1.induct)(auto)

lemma not_widen1_trancl_NT_Array:
  "[| (U, T) ∈ (widen1 P)^+; is_NT_Array T |] ==> False"
by(induct rule: trancl.induct)(auto intro: not_widen1_NT_Array)

lemma widen1_trancl_into_Array_widen1_trancl:
  "[| (A, B) ∈ (widen1 P)+; ¬ is_NT_Array A |] ==> (A⌊⌉, B⌊⌉) ∈ (widen1 P)+"
apply(induct rule: trancl.induct)
 apply(rule r_into_trancl)
 apply(erule widen1_Array_Array, assumption)
apply(clarsimp)
apply(case_tac "is_NT_Array b")
 apply(fastsimp dest: not_widen1_trancl_NT_Array)
apply(auto dest: widen1_Array_Array)
done

lemma widen1_rtrancl_into_Array_widen1_rtrancl:
  "[| (A, B) ∈ (widen1 P)*; ¬ is_NT_Array A |] ==> (A⌊⌉, B⌊⌉) ∈ (widen1 P)*"
apply(erule rtranclE)
 apply(simp)
apply(rule trancl_into_rtrancl)
apply(rule widen1_trancl_into_Array_widen1_trancl)
by(rule rtrancl_into_trancl1)

lemma Array_Object_widen1_trancl:
  assumes wf: "wf_prog wmdc P"
  and itA: "is_type P A"
  shows "(A⌊⌉, Class Object) ∈ (widen1 P)+"
proof(cases "is_NT_Array A")
  case True thus ?thesis
    by(auto intro: r_into_trancl widen1_NT_Object)
next
  case False
  show ?thesis using itA False
  proof(induct A)
    case (Class C)
    from `is_type P (Class C)`
    have "is_class P C" by simp
    hence "P \<turnstile> C \<preceq>* Object"
      by(rule subcls_C_Object[OF _ wf])
    hence "(Class C, Class Object) ∈ (widen1 P)*"
      by(rule subcls_into_widen1_rtrancl)
    hence "(Class C⌊⌉, Class Object⌊⌉) ∈ (widen1 P)*"
      by(auto intro: widen1_rtrancl_into_Array_widen1_rtrancl)
    with widen1_Array_Object[where P=P] is_class_Object[OF wf] 
    show ?case by(blast intro: rtrancl_into_trancl1)
  next
    case (Array A)
    note IH = `[| is_type P A; ¬ is_NT_Array A |] ==> (A⌊⌉, Class Object) ∈ (widen1 P)+`
    from `is_type P (A⌊⌉)` `¬ is_NT_Array (A⌊⌉)`
    have "(A⌊⌉, Class Object) ∈ (widen1 P)+"
      by(auto intro: IH)
    hence "(A⌊⌉, Class Object) ∈ (widen1 P)*"
      by(rule trancl_into_rtrancl)
    hence "(A⌊⌉⌊⌉, Class Object⌊⌉) ∈ (widen1 P)*" using `¬ is_NT_Array (A⌊⌉)`
      by(rule widen1_rtrancl_into_Array_widen1_rtrancl)
    with widen1_Array_Object[where P=P] is_class_Object[OF wf]
    show ?case by (blast intro: rtrancl_into_trancl1)
  qed(auto intro: widen1.intros)
qed

lemma widen_into_widen1_trancl:
  assumes wf: "wf_prog wfmd P"
  shows "[| P \<turnstile> A ≤ B; A ≠ B; A ≠ NT |] ==> (A, B) ∈ (widen1 P)^+"
proof(induct rule: widen.induct)
  case (widen_subcls C D)
  from `Class C ≠ Class D` `P \<turnstile> C \<preceq>* D`
  have "(subcls1 P)++ C D"
    by(auto elim: rtranclp.cases intro: rtranclp_into_tranclp1)
  thus ?case
    by(rule subcls1_trancl_widen1_trancl)
next
  case (widen_array_object A)
  thus ?case
    by - (rule Array_Object_widen1_trancl[OF wf])
next
  case (widen_array_array A B)
  hence "(A, B) ∈ (widen1 P)+" by(cases A, auto)
  with `¬ is_NT_Array A` show "(A⌊⌉, B⌊⌉) ∈ (widen1 P)+"
    by -(rule widen1_trancl_into_Array_widen1_trancl)
qed(fastsimp)+


lemma wf_prog_impl_acc_widen:
  assumes wfP: "wf_prog wfmd P"
  shows "acc (widen P)"
proof -
  from wf_converse_widen1[OF wfP]
  have "wf (((widen1 P)^-1)^+)"
    by(rule wf_trancl)
  hence wfw1t: "!!M T. T ∈ M ==> (∃z∈M. ∀y. (y, z) ∈ ((widen1 P)¯)+ --> y ∉ M)"
    by(auto simp only: wf_eq_minimal)
  { fix M T
    assume TM: "(T :: ty) ∈ M"
    have "∃z∈M. ∀y. (y, z) ∈ {(y, T). widen P T y ∧ T ≠ y} --> y ∉ M"
    proof(cases "(∃C. Class C ∈ M) ∨ (∃U. U⌊⌉ ∈ M)")
      case False
      thus ?thesis
        by -(rule bexI[OF _ TM], cases T, auto simp: NT_widen Class_widen2 dest: Array_widen)
    next
      case True
      have BNTthesis: "!!B. B ∈ M - {NT} ==> ?thesis"
      proof -
        fix B
        assume BM: "B ∈ M - {NT}"
        from wfw1t[OF BM] obtain z
          where zM: "z ∈ M"
          and znnt: "z ≠ NT"
          and y: "!!y. (y, z) ∈ ((widen1 P)¯)+ ==> y ∉ M - {NT}" by blast
        show "?thesis B"
        proof(rule bexI[OF _ zM], rule allI, rule impI)
          fix y
          assume "(y, z) ∈ {(y, T). widen P T y ∧ T ≠ y}"
          hence Pzy: "P \<turnstile> z ≤ y" and zy: "z ≠ y" by auto
          hence "(z, y) ∈ (widen1 P)^+" using znnt
            by(rule widen_into_widen1_trancl[OF wfP])
          hence ynM: "y ∉ M - {NT}"
            by -(rule y, simp add: trancl_converse)
          with Pzy znnt show "y ∉ M" by(auto)
        qed
      qed
      from True show ?thesis
        by -(erule disjE, (erule exE, rule BNTthesis, fastsimp)+)
    qed }
  hence "wf {(y, x). widen P x y ∧ x ≠ y}"
    by(clarsimp simp only: wf_eq_minimal)
  thus ?thesis
    by(unfold Semilat.acc_def lesssub_def lesub_def)
qed


lemmas wf_widen_acc = wf_prog_impl_acc_widen
declare wf_widen_acc [intro, simp]

lemma acyclic_widen1:
  "wf_prog wfmc P ==> acyclic (widen1 P)"
by(auto dest: wf_converse_widen1 wf_acyclic simp add: acyclic_converse)


lemma widen1_into_widen:
  assumes wf: "wf_prog wf_mb P"
  shows "(A, B) ∈ widen1 P ==> P \<turnstile> A ≤ B"
apply(induct rule: widen1.induct)
apply(insert is_class_Object[OF wf])
apply(auto intro: widen.intros NT_Array_is_type elim: widen1.cases)
done

lemma widen1_rtrancl_into_widen:
  assumes wf: "wf_prog wf_mb P"
  shows "(A, B) ∈ (widen1 P)^* ==> P \<turnstile> A ≤ B"
apply(induct rule: rtrancl_induct)
apply(insert wf)
by(auto dest!: widen1_into_widen elim: widen_trans)

lemma widen_eq_widen1_trancl:
  "[| wf_prog wf_md P; T ≠ NT; T ≠ U |] ==> P \<turnstile> T ≤ U <-> P \<turnstile> T <+ U"
apply(rule iffI)
 apply(rule widen_into_widen1_trancl, assumption+)
apply(erule widen1_rtrancl_into_widen)
by(rule trancl_into_rtrancl)

lemma closed_err_types:
  assumes wfP: "wf_prog wf_mb P"
  shows "closed (err (is_type P)) (lift2 (sup P))"
proof -
  { fix A B
    assume itA: "is_type P A"
      and itB: "is_type P B"
      and ANT: "A ≠ NT"
      and BNT: "B ≠ NT"
      and AnB: "A ≠ B"
      and RTA: "is_refT A"
      and RTB: "is_refT B"
    hence AObject: "P \<turnstile> A ≤ Class Object"
      and BObject: "P \<turnstile> B ≤ Class Object"
      by(auto intro: is_refType_widen_Object[OF wfP])
    have "is_type P (exec_lub (widen1 P) (super P) A B)"
    proof(cases "A = Class Object ∨ B = Class Object")
      case True
      hence "exec_lub (widen1 P) (super P) A B = Class Object"
      proof(rule disjE)
        assume A: "A = Class Object"
        moreover
        from BObject BNT
        have "(B, Class Object) ∈ (widen1 P)*"
          by(cases "B = Class Object", auto intro: trancl_into_rtrancl widen_into_widen1_trancl[OF wfP])
        hence "is_ub ((widen1 P)*) (Class Object) B (Class Object)"
          by(auto intro: is_ubI)
        hence "is_lub ((widen1 P)*) (Class Object) B (Class Object)"
          by(auto simp add: is_lub_def dest: is_ubD)
        with acyclic_widen1[OF wfP]
        have "exec_lub (widen1 P) (super P) (Class Object) B = Class Object"
          by(auto intro: exec_lub_conv superI)
        ultimately show "exec_lub (widen1 P) (super P) A B = Class Object" by simp
      next
        assume B: "B = Class Object"
        moreover
        from AObject ANT
        have "(A, Class Object) ∈ (widen1 P)*"
          by(cases "A = Class Object", auto intro: trancl_into_rtrancl widen_into_widen1_trancl[OF wfP])
        hence "is_ub ((widen1 P)*) (Class Object) A (Class Object)"
          by(auto intro: is_ubI)
        hence "is_lub ((widen1 P)*) (Class Object) A (Class Object)"
          by(auto simp add: is_lub_def dest: is_ubD)
        with acyclic_widen1[OF wfP]
        have "exec_lub (widen1 P) (super P) A (Class Object) = Class Object"
          by(auto intro: exec_lub_conv superI)
        ultimately show "exec_lub (widen1 P) (super P) A B = Class Object" by simp
      qed
      with wfP show ?thesis by(simp)
    next
      case False
      hence AnObject: "A ≠ Class Object"
        and BnObject: "B ≠ Class Object" by auto
      from widen_into_widen1_trancl[OF wfP AObject AnObject ANT]
      have "(A, Class Object) ∈ (widen1 P)*"
        by(rule trancl_into_rtrancl)
      moreover from widen_into_widen1_trancl[OF wfP BObject BnObject BNT]
      have "(B, Class Object) ∈ (widen1 P)*"
        by(rule trancl_into_rtrancl)
      ultimately have "is_lub ((widen1 P)*) A B (exec_lub (widen1 P) (super P) A B)"
        apply(rule is_lub_exec_lub[OF single_valued_widen1[OF wfP] acyclic_widen1[OF wfP]])
        by(auto intro: superI)
      hence Aew1: "(A, exec_lub (widen1 P) (super P) A B) ∈ (widen1 P)*"
        by(auto simp add: is_lub_def dest!: is_ubD)
      thus ?thesis
      proof(rule rtranclE)
        assume "A = exec_lub (widen1 P) (super P) A B"
        with itA show ?thesis by simp
      next
        fix A'
        assume "P \<turnstile> A' <1 exec_lub (widen1 P) (super P) A B"
        thus ?thesis by(rule widen1_is_type[OF wfP])
      qed
    qed }
  with is_class_Object[OF wfP] show ?thesis
    unfolding closed_def plussub_def lift2_def sup_def
    by(auto split: err.split ty.splits)(auto simp add: mem_def exec_lub_refl)
qed

lemma widen_into_widen1_rtrancl:
  "[|wf_prog wfmd P; widen P A B; A ≠ NT|] ==> (A, B) ∈ (widen1 P)*"
apply(cases "A = B", auto intro: trancl_into_rtrancl widen_into_widen1_trancl)
done


lemma sup_widen_greater:
  assumes wfP: "wf_prog wf_mb P"
  and it1: "is_type P t1"
  and it2: "is_type P t2"
  and sup: "sup P t1 t2 = OK s"
  shows "widen P t1 s ∧ widen P t2 s"
proof -
  have "[| is_refT t1; is_refT t2; t1 ≠ NT; t2 ≠ NT |]
    ==> P \<turnstile> t1 ≤ exec_lub (widen1 P) (super P) t1 t2
     ∧ P \<turnstile> t2 ≤ exec_lub (widen1 P) (super P) t1 t2"
  proof -
    assume t1: "is_refT t1"
      and t2: "is_refT t2"
      and t1NT: "t1 ≠ NT"
      and t2NT: "t2 ≠ NT"
    with it1 it2 wfP have "P \<turnstile> t1 ≤ Class Object" "P \<turnstile> t2 ≤ Class Object"
      by(auto intro: is_refType_widen_Object)
    with t1NT t2NT have "(t1, Class Object) ∈ (widen1 P)^*" "(t2, Class Object) ∈ (widen1 P)^*"
      by(auto intro: widen_into_widen1_rtrancl[OF wfP])
    with single_valued_widen1[OF wfP]
    obtain u where
      "is_lub ((widen1 P)^* ) t1 t2 u" 
      by (blast dest: single_valued_has_lubs)
    moreover
    note acyclic_widen1[OF wfP]
    moreover
    have "∀x y. (x, y) ∈ widen1 P --> super P x = y"
      by (blast intro: superI)
    ultimately
    show "P \<turnstile> t1 ≤ exec_lub (widen1 P) (super P) t1 t2 ∧
          P \<turnstile> t2 ≤ exec_lub (widen1 P) (super P) t1 t2"
      by (simp add: exec_lub_conv) (blast dest: is_lubD is_ubD intro: widen1_rtrancl_into_widen[OF wfP])
  qed
  with it1 it2 sup show ?thesis
    by(cases s, auto simp add: sup_def split: split_if_asm elim: refTE)
qed

lemma sup_widen_smallest:
  assumes wfP: "wf_prog wf_mb P"
  and itT: "is_type P T"
  and itU: "is_type P U"
  and itV: "is_type P V"
  and TwV: "P \<turnstile> T ≤ V"
  and UwV: "P \<turnstile> U ≤ V"
  and sup: "sup P T U = OK W"
  shows "widen P W V"
proof -
  { assume rT: "is_refT T"
      and rU: "is_refT U"
      and UNT: "U ≠ NT"
      and TNT: "T ≠ NT"
      and W: "exec_lub (widen1 P) (super P) T U = W"
    from itU itT rT rU UNT TNT have "P \<turnstile> T ≤ Class Object" "P \<turnstile> U ≤ Class Object"
      by(auto intro:is_refType_widen_Object[OF wfP])
    with UNT TNT have "(T, Class Object) ∈ (widen1 P)^*" "(U, Class Object) ∈ (widen1 P)^*"
      by(auto intro: widen_into_widen1_rtrancl[OF wfP])
    with single_valued_widen1[OF wfP]
    obtain X where
      lub: "is_lub ((widen1 P)^* ) T U X"
      by (blast dest: single_valued_has_lubs)   
    with acyclic_widen1[OF wfP]
    have "exec_lub (widen1 P) (super P) T U = X"
      by (blast intro: superI exec_lub_conv)
    moreover
    from TwV TNT UwV UNT have "(T, V) ∈ (widen1 P)^*" "(U, V) ∈ (widen1 P)^*"
      by(auto intro: widen_into_widen1_rtrancl[OF wfP])
    with lub have "(X, V) ∈ (widen1 P)^*" 
      by (clarsimp simp add: is_lub_def is_ub_def)
    ultimately
    have "(exec_lub (widen1 P) (super P) T U, V) ∈ (widen1 P)^*"
      by blast
    hence "P \<turnstile> exec_lub (widen1 P) (super P) T U ≤ V"
      by(rule widen1_rtrancl_into_widen[OF wfP])
    with W have "P \<turnstile> W ≤ V" by simp }
  with sup itT itU itV TwV UwV show ?thesis
    by(simp add: sup_def split: split_if_asm)
qed

lemma sup_exists:
  "[| widen P a c; widen P b c |] ==> EX T. sup P a b = OK T"
(*<*)
apply (unfold sup_def)
apply (cases b)
apply auto
apply (cases a)
apply auto
apply (cases a)
apply auto
apply(cases a)
apply(auto)
done
(*>*)

lemma err_semilat_JType_esl:
  "wf_prog wf_mb P ==> err_semilat (esl P)"
proof -
  assume wf_prog: "wf_prog wf_mb P"  
  hence "order (widen P)"..
  moreover from wf_prog
  have "closed (err (is_type P)) (lift2 (sup P))"
    by (rule closed_err_types)
  moreover
  from wf_prog have
    "(∀x∈err (is_type P). ∀y∈err (is_type P). x \<sqsubseteq>Err.le (widen P) x \<squnion>lift2 (sup P) y) ∧ 
     (∀x∈err (is_type P). ∀y∈err (is_type P). y \<sqsubseteq>Err.le (widen P) x \<squnion>lift2 (sup P) y)"
    by(auto simp add: lesub_def plussub_def Err.le_def lift2_def sup_widen_greater mem_def split: err.split)
  moreover from wf_prog have
    "∀x∈err (is_type P). ∀y∈err (is_type P). ∀z∈err (is_type P). 
    x \<sqsubseteq>Err.le (widen P) z ∧ y \<sqsubseteq>Err.le (widen P) z --> x \<squnion>lift2 (sup P) y \<sqsubseteq>Err.le (widen P) z"
    by (unfold lift2_def plussub_def lesub_def Err.le_def)
       (auto intro: sup_widen_smallest dest:sup_exists simp add: mem_def split: err.split)
  ultimately show ?thesis by (simp add: esl_def semilat_def sl_def Err.sl_def)
qed

end