| |
conforms.art
notation syntax rsyntax rels runtime rstatics conforms ;
import syntax rsyntax runtime0 wf wfenv conforms rstatics1 link2_0 statics1 ;

// value conformance implies weak conformance

thm weak-from-val
if (TE,heap) |- val val_conforms_to vt a
then (TE,heap) |- val wconforms_to vt
qed by rule cases on a;
thm typing-from-val
if (TE,heap) |- val val_conforms_to vt a
then ?vt'. (TE,heap) |- val rval_hastype vt' & TE |- vt' varty_widens_to vt;
qed by weak-from-val,WCONFORMS_TO,a;

// transitivity of weak conformance.

thm wconforms-trans
if TE wf_tyenv TE_wf
(TE,heap) |- val wconforms_to val_ty val_conforms
TE |- val_ty varty_widens_to val_ty' widens1
then (TE,heap) |- val wconforms_to val_ty' goal;
qed by WCONFORMS_TO,varty_widens_to-trans, val_conforms, widens1,TE_wf;

// value typing implies weak value conformance at the same value.
// It does *not* imply strong value conformance except when the heap conforms!

thm weak-from-typing
if TE wf_tyenv TE_wf
(TE,heap) |- val rval_hastype val_ty val_types
then (TE,heap) |- val wconforms_to val_ty;
qed by WCONFORMS_TO,varty_widens_to-refl, val_types;

// transitivity of strong value conformance.

thm val_conforms-trans
if TE wf_tyenv TE_wf
(TE,heap) |- val val_conforms_to val_ty val_conforms
TE |- val_ty varty_widens_to val_ty' widens1
then (TE,heap) |- val val_conforms_to val_ty' goal;
have (TE,heap) |- val wconforms_to val_ty'
by rule cases on val_conforms, wconforms-trans,widens1,TE_wf;
then qed by rule cases on val_conforms,
val_conforms_to__Prim [TE,heap,val_ty'],
val_conforms_to__Object [TE,heap,val_ty'/vt],
val_conforms_to__Array [TE,heap,val_ty'/vt],
val_conforms_to__Null [TE,heap,val_ty']; // true - meson would get this but tab won't!

// value typing implies weak value conformance at the same value.
// It does *not* imply strong value conformance except when the heap conforms!

thm exp_typing-from-weak
if TE wf_tyenv
(TE,heap) |- val wconforms_to valty
then ?expty. (TE,FT,heap) |- RValue(val) rexp_hastype expty &
TE |- expty expty_widens_to SOME(valty);
then qed by expty_widens_to__Some, rstatics__Value, WCONFORMS_TO__rule

// Simple facts about heap conformance

thm heap_leq-refl
TE |- heap0 heap_leq heap0;
qed by [heap0/heap1]

// And here is the case where the heap conforms, and so typing
// implies strong value conformance.

thm conforms-from-typing
if TE wf_tyenv TE_wf
(TE,heap) |- val rval_hastype val_ty val_types
TE |- heap heap_conforms heap_conforms
then (TE,heap) |- val val_conforms_to val_ty g;
have (TE,heap) |- val wconforms_to val_ty by weak-from-typing,wconforms-trans,val_types,TE_wf;
per cases
case val = RAddr(SOME(addr))
FPLOOKUP heap addr = SOME(OBJECT(fldvals,C)) lookup
val_ty = VT(Class(C),0) ;
qed by HEAP_CONFORMS__rule [TE,heap,addr],ADDR_CONFORMS__object [TE,heap,addr],lookup,heap_conforms,IN_FPDOM;
case val = RAddr(SOME(addr))
FPLOOKUP heap addr = SOME(ARRAY(VT(simpty,dim),vec)) lookup
val_ty = VT(simpty,ndim)
ndim = dim + 1 ;
qed by HEAP_CONFORMS__rule [TE,heap,addr],ADDR_CONFORMS__array [TE,heap,addr],lookup,heap_conforms,IN_FPDOM;
end by rule cases on val_types,g,
heap_conforms, TE_wf,weak-from-typing, val_types,
val_conforms_to__Prim [TE,heap],
val_conforms_to__Null [TE,heap];

//

thm object-fields-conform
if TE |- heap heap_conforms heap_conforms
FPLOOKUP heap addr = SOME(OBJECT(fldvals,C)) [rw,auto]
(idx,varty) :: FDecs(TE,C) is_field
fldvals idx = SOME(val) [rw,auto]
then (TE,heap) |- val wconforms_to varty;
have addr ::: FPDOM(heap) by IN_FPDOM;
then have (TE,heap) |- RAddr(SOME(addr)) val_conforms_to VT(Class(C),0) addr_conforms
by HEAP_CONFORMS,ADDR_CONFORMS__object [heap,TE,addr,fldvals],heap_conforms;
have (TE,heap) |- fldvals fldvals_wconform_to C
by rule cases on addr_conforms;
then have
!idx vt'. FDecs(TE,C)(idx,vt') -->
?val. fldvals idx = SOME(val) &
(TE,heap) |- val wconforms_to vt' fields_conform
by FLDVALS_WCONFORM_TO;
qed by hard is_field,IN_ELIM,fields_conform [idx,varty]

// this says that if an array conforms, then all its elements weakly conform.

thm array-elements-conform
if TE |- heap heap_conforms heap_conforms
FPLOOKUP heap addr = cell cell
cell = SOME(ARRAY(vt,vec)) lookup
j < LEN(vec) is_in_vec
then (TE,heap) |- EL(j)(vec) wconforms_to vt;
let vt = VT(simpty,dim) ;
have addr ::: FPDOM(heap) by IN_FPDOM,lookup,cell;
then have (TE,heap) |- RAddr(SOME(addr)) val_conforms_to VT(simpty,dim+1) addr_conforms
by HEAP_CONFORMS,ADDR_CONFORMS__array [heap,TE,addr],lookup,cell,heap_conforms;
consider fldvals',C'',vt',vec' st
(cell = SOME(OBJECT(fldvals',C''))) |
(cell = SOME(ARRAY(vt',vec')) & (TE,heap) |- vec' els_wconform_to vt')
by rule cases on addr_conforms,cell;
then qed by lookup, is_in_vec,IN_ELIM,ELS_WCONFORM_TO

// Simple facts about frame typing conformance

thm ftyenv_leq-refl
FT ftyenv_leq FT;
qed by ftyenv_leq [FT/FT1]

// Monotonicity of value typing under stack/heap widening, up to widening

thm val-mono-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
TE |- heap0 heap_leq heap1 heap1_bigger
(TE,heap0) |- val rval_hastype val_ty val_types_in_heap0
then ?val_ty'. (TE,heap1) |- val rval_hastype val_ty' &
TE |- val_ty' varty_widens_to val_ty valgoal;
// most the cases are trivial because the state is not used. We
// only have to consider the two heap object cases,
// where the result follows easily by the definition of heap_leq.
per cases
case val = RAddr(SOME(addr))
FPLOOKUP heap0 addr = SOME(OBJECT(fldvals,C)) lookup
val_ty = VT(Class(C),0) ;
have addr ::: FPDOM(heap0) in_heap by IN_ELIM,PDOM,lookup;
then have (TE,heap0) |- val val_conforms_to val_ty by HEAP_CONFORMS,ADDR_CONFORMS__object,lookup,heap0_conforms;
then have (TE,heap1) |- val val_conforms_to val_ty by HEAP_LEQ__rule [addr,heap0/heap0,heap1/heap1],val_types_in_heap0,heap1_bigger,in_heap;
then qed by weak-from-val,typing-from-val;
case val = RAddr(SOME(addr))
FPLOOKUP heap0 addr = SOME(ARRAY(VT(simpty,dim),vec)) lookup
val_ty = VT(simpty,ndim)
ndim = dim + 1 ;
have addr ::: FPDOM(heap0) in_heap by IN_ELIM,PDOM,lookup;
then have (TE,heap0) |- val val_conforms_to val_ty by HEAP_CONFORMS,ADDR_CONFORMS__array,lookup,heap0_conforms;
then have (TE,heap1) |- val val_conforms_to val_ty by HEAP_LEQ__rule [addr,heap0/heap0,heap1/heap1],val_types_in_heap0,heap1_bigger,in_heap;
then qed by weak-from-val,typing-from-val;
end by rule cases on val_types_in_heap0,
valgoal [val_ty], varty_widens_to-refl,
rval_hastype__NullToClass [TE,heap1],
rval_hastype__NullToInterface [TE,heap1],
rval_hastype__NullToArray [TE,heap1],
rval_hastype__Prim [TE,heap1];

// Monotonicity of typing under stack/heap widening, up to widening
//
// This proof subsumes several of the theorems in the abstract.

thm exp-mono-lemma // and , var-mono-lemma, stmt-mono-lemma, StackVar-mono-lemma, Field-mono-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
FT0 ftyenv_leq FT1 FT1_bigger
TE |- heap0 heap_leq heap1 heap1_bigger
then {
if (TE,FT0,heap0) |- var rvar_hastype var_ty var_types_in_heap0
then ?var_ty'. (TE,FT1,heap1) |- var rvar_hastype var_ty' &
TE |- var_ty' varty_widens_to var_ty &
(!obj' C' f'. var = RField(obj',C',f') --> var_ty' = var_ty) &
(!fidx' id'. var = RStackVar(fidx',id') --> var_ty' = var_ty) vargoal
} and {
if (TE,FT0,heap0) |- exp rexp_hastype exp_ty exp_types_in_heap0
then ?exp_ty'. (TE,FT1,heap1) |- exp rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to exp_ty expgoal
} and {
if (TE,FT0,heap0) |- stmt rstmt_hastype stmt_types_in_heap0
then (TE,FT1,heap1) |- stmt rstmt_hastype
}
proceed by rule induction on
var_types_in_heap0 with var,var_ty variable,
exp_types_in_heap0 with exp,exp_ty variable,
stmt_types_in_heap0 with stmt variable;
case StackVar
var = RStackVar(frame,id)
frame < LEN(FT0)
EL(frame)(FT0) = FS FS
PLOOKUP(FS)(id) = SOME(var_ty) ;
have FS = EL(frame)(FT1)
frame < LEN(FT1) by hard FT1_bigger,ftyenv_leq,FS,arith12;
then qed by rstatics__StackVar, vargoal [var_ty], varty_widens_to-refl
case Access
var = RAccess(arr,idx)
var_ty = VT(simpty,ndim)
?exp_ty'. (TE,FT1,heap1) |- arr rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to SOME(VT(simpty,dim)) arr_ihyp
(TE,FT0,heap0) |- arr rexp_hastype SOME(VT(simpty,dim)) arr_ty
0 < dim
?exp_ty'. (TE,FT1,heap1) |- idx rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to SOME(VT(PrimT(intT),0)) idx_ihyp
(TE,FT0,heap0) |- idx rexp_hastype SOME(VT(PrimT(intT),0)) idx_ty
ndim = dim - 1 ;
let int = SOME(VT(PrimT(intT),0)) ;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider arr_ty1 st
(TE,FT1,heap1) |- arr rexp_hastype arr_ty1 arr_types_in_s1
TE |- arr_ty1 expty_widens_to SOME(VT(simpty,dim)) arr_ty1_narrower by arr_ihyp;
consider simpty1 st
arr_ty1 = SOME(VT(simpty1,dim))
TE |- simpty1 simpty_widens_to simpty simpty1_narrower
by array-widens-lemma [TE,arr_ty1,simpty,dim],arr_ty1_narrower;
consider idx_ty1 st
(TE,FT1,heap1) |- idx rexp_hastype idx_ty1 idx_types_in_s1
TE |- idx_ty1 expty_widens_to int idx_ty1_narrower by idx_ihyp;
have idx_ty1 = int by idx_ty1_narrower,prim-widens-lemma;
have TE |- VT(simpty1,ndim) varty_widens_to VT(simpty,ndim) by varty_widens_to__Simple,simpty1_narrower;
then qed by rstatics__Access [dim/dim], vargoal [VT(simpty1,ndim)], arr_types_in_s1, idx_types_in_s1
case Field
var = RField(obj,C',fld)
?exp_ty'. (TE,FT1,heap1) |- obj rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to SOME(VT(Class(C0),0)) obj_ihyp
(TE,FT0,heap0) |- obj rexp_hastype SOME(VT(Class(C0),0)) obj_types
((C',fld),var_ty) :: FDecs(TE,C0) fld_visible;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider obj_ty1 st
(TE,FT1,heap1) |- obj rexp_hastype obj_ty1 obj_types_in_s1
TE |- obj_ty1 expty_widens_to SOME(VT(Class(C0),0)) obj_ty1_narrower by obj_ihyp;
// C can't be Object, 'cos its got an entry in the env.
per cases
case C0 = Object ;
contradiction by TE_wf,FDecs-Object [TE],fld_visible;
case C0 <> Object ;
consider C1 st
TE |- C1 subclass_of C0 C1_subclass
obj_ty1 = SOME(VT(Class(C1),0))
by class-widens-lemma [C0,TE,obj_ty1],TE_wf,obj_ty1_narrower
have ((C',fld),var_ty) :: FDecs(TE,C1)
by inherited-fields-exist [TE],C1_subclass,fld_visible,TE_wf;
then qed by rstatics__Field [C'/C',C1/C],
vargoal [var_ty], obj_types_in_s1
end;
case Var
exp = RVar(svar)
exp_ty = SOME(svar_ty)
?var_ty'. (TE,FT1,heap1) |- svar rvar_hastype var_ty' &
TE |- var_ty' varty_widens_to svar_ty &
(!obj' C' f'. svar = RField(obj',C',f') --> var_ty' = svar_ty) &
(!fidx' id'. svar = RStackVar(fidx',id') --> var_ty' = svar_ty) svar_ihyp
(TE,FT0,heap0) |- svar rvar_hastype svar_ty svar_types;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider svar_ty1 st
(TE,FT1,heap1) |- svar rvar_hastype svar_ty1 svar_types_in_s1
TE |- svar_ty1 varty_widens_to svar_ty svar_ty1_narrower by svar_ihyp;
then have TE |- SOME(svar_ty1) expty_widens_to exp_ty wider by expty_widens_to__Some;
qed by rstatics__Var, expgoal [SOME(svar_ty1)], svar_types_in_s1, wider;
case Void
exp = RVoid
exp_ty = NONE ;
qed by rstatics__Void, expgoal [NONE:varType option]
case NewClass
exp = RNewClass(C,flds)
exp_ty = SOME(VT(Class(C),0))
!fld vt. (fld,vt) :: FDecs(TE,C) --> TE |- vt wf_vartype fields_wf
flds = PGRAPH {fspec | FDecs(TE,C)(fspec)} ;
qed by rstatics__NewClass [TE,heap1,FT1], expgoal [exp_ty], expty_widens_to-refl,fields_wf
case NewArray
exp = RNewArray(st,dims,ext)
exp_ty = SOME(VT(st,dim))
dim = LEN(dims)+ext
0 < dim
TE |- st wf_simptype
!j. j < LEN(dims) -->
(?exp_ty'. (TE,FT1,heap1) |- EL(j)(dims) rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to (SOME(VT(PrimT(intT),0)))) &
(TE,FT0,heap0) |- (EL(j)(dims)) rexp_hastype (SOME(VT(PrimT(intT),0))) ihyp;
let int = SOME(VT(PrimT(intT),0)) ;
have !j. j < LEN(dims) --> (TE,FT1,heap1) |- EL(j)(dims) rexp_hastype int
proof
then consider j st
+ j < LEN(dims)
- (TE,FT1,heap1) |- EL(j)(dims) rexp_hastype int ;
consider exp_ty' st
(TE,FT1,heap1) |- EL(j)(dims) rexp_hastype exp_ty'
TE |- exp_ty' expty_widens_to int by ihyp [j];
then qed by prim-widens-lemma [TE,intT,exp_ty']; ;
then qed by rstatics__NewArray [heap1,TE,FT1,ext/ext,LEN(dims)+ext/dim,dims,st],
expgoal [exp_ty], expty_widens_to-refl;
case Call
// Consider the case where all the arguments are ground.
// A Null excpeption may occur, otherwise get the program body
// and use lemmas that demonstrate that this has the right type.
exp = RCall(obj,AT,m,args)
exp_ty = rt0
LEN(args) = nargs [rw,auto]
LEN(AT) = nargs [rw,auto]
?exp_ty'. (TE,FT1,heap1) |- obj rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to SOME(vt0) obj_ihyp
(TE,FT0,heap0) |- obj rexp_hastype SOME(vt0) obj0_types_in_heap0
MSigs(TE,vt0)(m,MT(AT,rt0)) m_visible_from_vt0
!j. j < nargs -->
?varty.((?exp_ty'. (TE,FT1,heap1) |- EL(j)(args) rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to SOME(varty)) &
(TE,FT0,heap0) |- EL(j)(args) rexp_hastype SOME(varty)) &
TE |- varty varty_widens_to EL(j)(AT) ihyp_for_args ;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
let obj_ty0 = SOME(vt0) ;
consider obj_ty1 st
(TE,FT1,heap1) |- obj rexp_hastype obj_ty1 obj_types_in_s1
TE |- obj_ty1 expty_widens_to obj_ty0 obj_ty1_narrower by obj_ihyp;
consider vt1 st
obj_ty1 = SOME(vt1)
TE |- vt1 varty_widens_to vt0 vt1_narrower by rule cases on obj_ty1_narrower,PEXISTS_SPLIT;
// now establish new types for each argument
have !j. j < LEN(args) -->
?varty. (TE,FT1,heap1) |- EL(j)(args) rexp_hastype SOME(varty) &
TE |- varty varty_widens_to EL(j)(AT) args_type_in_s1
proof
then consider j st
+ j < LEN(args)
- ?varty.
(TE,FT1,heap1) |- EL(j)(args) rexp_hastype SOME(varty) &
TE |- varty varty_widens_to EL(j)(AT);
consider varty0, expty1 st
(TE,FT1,heap1) |- EL(j)(args) rexp_hastype expty1 argj_types_in_s1
TE |- expty1 expty_widens_to SOME(varty0) expty1_narrower
TE |- varty0 varty_widens_to EL(j)(AT) varty0_fits by ihyp_for_args [j];
consider varty1 st
expty1 = SOME(varty1)
TE |- varty1 varty_widens_to varty0 varty1_narrower by rule cases on expty1_narrower,PEXISTS_SPLIT;
have TE |- varty1 varty_widens_to EL(j)(AT) varty1_fits
by varty0_fits,
varty_widens_to-trans [TE,varty1/vt1,EL(j)(AT)/vt3,varty0/vt2],
varty1_narrower,TE_wf;
// BUG: this should not not need explicit instantiation.
qed by varty1_fits,argj_types_in_s1; ;
consider rt1 st
MSigs(TE,vt1)(m,MT(AT,rt1)) m_visible_from_vt1
TE |- rt1 expty_widens_to rt0 rt1_narrower
by inherited-methods-exist [TE],m_visible_from_vt0,TE_wf,vt1_narrower
qed by rstatics__Call [TE,rt1,heap1,FT1,vt1,args,AT,obj], expgoal [rt1],
args_type_in_s1,obj_types_in_s1,m_visible_from_vt1, rt1_narrower;
case Body
exp = RBody(rstmt,rexp)
(TE,FT1,heap1) |- rstmt rstmt_hastype rstmt_types_in_s1
(TE,FT0,heap0) |- rstmt rstmt_hastype rstmt_types_in_heap0
?exp_ty'. (TE,FT1,heap1) |- rexp rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to exp_ty rexp_ihyp
(TE,FT0,heap0) |- rexp rexp_hastype exp_ty rexp0_types_in_heap0;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider exp_ty1 st
(TE,FT1,heap1) |- rexp rexp_hastype exp_ty1 rexp_types_in_s1
TE |- exp_ty1 expty_widens_to exp_ty exp_ty1_narrower by rexp_ihyp;
qed by rstatics__Body, expgoal [exp_ty1], exp_ty1_narrower, rexp_types_in_s1, rstmt_types_in_s1;
// ASSIGNMENTS
// Note that addr := val is not well typed.
case AssignToStackVar
stmt = RAssign(RStackVar(fidx,id),rexp)
?exp_ty'. (TE,FT1,heap1) |- rexp rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to SOME(rexp_vty) rexp_ihyp
(TE,FT0,heap0) |- rexp rexp_hastype SOME(rexp_vty) rexp0_types
?var_ty'. (TE,FT1,heap1) |- RStackVar(fidx,id) rvar_hastype var_ty' &
TE |- var_ty' varty_widens_to lval_vty0 &
(!obj' C' f'. RStackVar(fidx,id) = RField(obj',C',f') --> var_ty' = lval_vty0) &
(!fidx' id'. RStackVar(fidx,id) = RStackVar(fidx',id') --> var_ty' = lval_vty0) lval_ihyp
(TE,FT0,heap0) |- RStackVar(fidx,id) rvar_hastype lval_vty0 lval_types_in_heap0
TE |- rexp_vty varty_widens_to lval_vty0 rexp0_fits;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider exp_ty1 st
(TE,FT1,heap1) |- rexp rexp_hastype exp_ty1 rexp_types_in_s1
TE |- exp_ty1 expty_widens_to SOME(rexp_vty) exp_ty1_narrower by rexp_ihyp;
consider exp_vty1 st
exp_ty1 = SOME(exp_vty1)
TE |- exp_vty1 varty_widens_to rexp_vty exp_vty1_narrower by rule cases on exp_ty1_narrower,PEXISTS_SPLIT;
have TE |- exp_vty1 varty_widens_to lval_vty0 rexp1_fits by varty_widens_to-trans,exp_vty1_narrower,rexp0_fits,TE_wf;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
have (TE,FT1,heap1) |- RStackVar(fidx,id) rvar_hastype lval_vty0 lval_types_in_s1
TE |- lval_vty0 varty_widens_to lval_vty0 lval_vty0_narrower by lval_ihyp;
qed by rstatics__AssignToStackVar [exp_vty1/varty', lval_vty0/varty], lval_vty0_narrower, lval_types_in_s1, rexp_types_in_s1, rexp1_fits;
case AssignToField
stmt = RAssign(RField(obj,C,fld),rexp)
?exp_ty'. (TE,FT1,heap1) |- rexp rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to SOME(rexp_vty) rexp_ihyp
(TE,FT0,heap0) |- rexp rexp_hastype SOME(rexp_vty) rexp_types_in_heap0
?var_ty'. (TE,FT1,heap1) |- RField(obj,C,fld) rvar_hastype var_ty' &
TE |- var_ty' varty_widens_to lval_vty0 &
(!obj' C' f'. RField(obj,C,fld) = RField(obj',C',f') --> var_ty' = lval_vty0) &
(!fidx' id'. RField(obj,C,fld) = RStackVar(fidx',id') --> var_ty' = lval_vty0) lval_ihyp
(TE,FT0,heap0) |- RField(obj,C,fld) rvar_hastype lval_vty0 lval0_types_in_heap0
TE |- rexp_vty varty_widens_to lval_vty0 rexp0_fits;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider exp_ty1 st
(TE,FT1,heap1) |- rexp rexp_hastype exp_ty1 rexp_types_in_s1
TE |- exp_ty1 expty_widens_to SOME(rexp_vty) exp_ty1_narrower by rexp_ihyp;
consider exp_vty1 st
exp_ty1 = SOME(exp_vty1)
TE |- exp_vty1 varty_widens_to rexp_vty exp_vty1_narrower by rule cases on exp_ty1_narrower,PEXISTS_SPLIT;
have TE |- exp_vty1 varty_widens_to lval_vty0 rexp1_fits by varty_widens_to-trans,exp_vty1_narrower,rexp0_fits,TE_wf;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
have (TE,FT1,heap1) |- RField(obj,C,fld) rvar_hastype lval_vty0 lval_types_in_s1
TE |- lval_vty0 varty_widens_to lval_vty0 lval_vty0_narrower by lval_ihyp;
qed by rstatics__AssignToField [exp_vty1/varty', lval_vty0/varty], lval_vty0_narrower, lval_types_in_s1, rexp_types_in_s1, rexp1_fits;
case AssignToArray
stmt = RAssign(RAccess(arr,idx),rexp)
?exp_ty'. (TE,FT1,heap1) |- rexp rexp_hastype exp_ty' &
TE |- exp_ty' expty_widens_to rexp_ty rexp_ihyp
(TE,FT0,heap0) |- rexp rexp_hastype rexp_ty rexp_types_in_heap0
?var_ty'. (TE,FT1,heap1) |- RAccess(arr,idx) rvar_hastype var_ty' &
TE |- var_ty' varty_widens_to lval_vty0 &
(!obj' C' f'. RAccess(arr,idx) = RField(obj',C',f') --> var_ty' = lval_vty0) &
(!fidx' id'. RAccess(arr,idx) = RStackVar(fidx',id') --> var_ty' = lval_vty0) lval_ihyp
(TE,FT0,heap0) |- RAccess(arr,idx) rvar_hastype lval_vty0 lval0_types_in_heap0;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider exp_ty1 st
(TE,FT1,heap1) |- rexp rexp_hastype exp_ty1 rexp_types_in_s1
TE |- exp_ty1 expty_widens_to rexp_ty exp_ty1_narrower by rexp_ihyp;
// tool failure: rigid specification of induction specs mean we have to repeat these to
// introduce names.
consider lval_vty1 st
(TE,FT1,heap1) |- RAccess(arr,idx) rvar_hastype lval_vty1 lval_types_in_s1 by lval_ihyp;
qed by rstatics__AssignToArray,lval_types_in_s1, rexp_types_in_s1;
// My dumb prover makes this case seem harder than it should!!
case Value
exp = RValue(val)
exp_ty = SOME(val_ty0)
(TE,heap0) |- val rval_hastype val_ty0 val_types_in_heap0;
consider val_ty1 st
(TE,heap1) |- val rval_hastype val_ty1
TE |- val_ty1 varty_widens_to val_ty0
by val-mono-lemma [TE,heap0/heap0,heap1/heap1],TE_wf,heap1_bigger,val_types_in_heap0,heap0_conforms;
qed by rstatics__Value,expty_widens_to__Some;
// this should be auotmatic - it isn't hard. Tab would get it - meson can't handle the
// simple equality reasoning.
case If; qed by prim-widens-lemma [TE,boolT],rstatics__If [TE,heap1,FT1];
// stmt = RIf(bexp,stmt1,stmt2)
// (TE,FT1,heap1) |- stmt1 rstmt_hastype
// (TE,FT0,heap0) |- stmt1 rstmt_hastype
// (TE,FT1,heap1) |- stmt2 rstmt_hastype
// (TE,FT0,heap0) |- stmt2 rstmt_hastype
// ?exp_ty'. (TE,FT1,heap1) |- bexp rexp_hastype exp_ty' &
// TE |- exp_ty' expty_widens_to SOME(VT(PrimT(boolT),0))
// (TE,FT0,heap0) |- bexp rexp_hastype SOME(VT(PrimT(boolT),0));
// consider exp_ty' (TE,FT1,heap1) |- bexp rexp_hastype exp_ty' &
// TE |- exp_ty' expty_widens_to SOME(VT(PrimT(boolT),0)) ;
// qed by prim-widens-lemma [TE,boolT,exp_ty'],rstatics__If [TE,heap1,FT1];
case Expr; qed by rstatics__Expr;
case Block; qed by rstatics__Block;
end;

// Monotonicity of weak value conformance under stack/heap widening

thm wconforms-mono-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
TE |- heap0 heap_leq heap1 heap1_bigger
(TE,heap0) |- val wconforms_to val_ty val_conforms_in_heap0
then (TE,heap1) |- val wconforms_to val_ty goal;
qed by WCONFORMS_TO,TE_wf,val-mono-lemma [TE,heap0/heap0,heap1/heap1,val],
val_conforms_in_heap0,heap1_bigger, heap0_conforms, varty_widens_to-trans;

// Monotonicity of value conformance under stack/heap widening

thm val_conforms-mono-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
TE |- heap0 heap_leq heap1 heap1_bigger
(TE,heap0) |- val val_conforms_to val_ty val_conforms_in_heap0
then (TE,heap1) |- val val_conforms_to val_ty goal;
// most the cases are trivial because the state is not used. We
// only have to consider the two heap object cases,
// where the result follows easily by the definition of heap_leq.
per cases
case val = RAddr(SOME(addr))
FPLOOKUP heap0 addr = SOME(heapobj) ;
have addr ::: FPDOM(heap0) by IN_FPDOM;
then qed by HEAP_LEQ__rule [heap0/heap0, heap1/heap1, addr],heap1_bigger,val_conforms_in_heap0;
end by rule cases on val_conforms_in_heap0,
goal, heap0_conforms,heap1_bigger, TE_wf,
val_conforms_to__Prim [TE,heap1],
val_conforms_to__Null [TE,heap1],
wconforms-mono-lemma [TE,heap0/heap0,heap1/heap1]

// Monotonicity of addr_conforms. NO!! Self-consistency is not implied by
// heap0 heap_leq heap1.

//proof of // addr_conforms-mono-lemma
//if TE wf_tyenv
// TE |- heap0 heap_conforms
// TE |- heap0 heap_leq heap1
// addr ::: FPDOM(heap0)
// (TE,heap0) |- addr addr_conforms
//then (TE,heap1) |- addr addr_conforms;

// Monotonicity of frames conformance under stack/heap widening

thm frames_conform-mono-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
TE |- heap0 heap_leq heap1 heap1_bigger
(TE,heap0) |- frames frames_conform_to FT frames_conform_in_heap0
then (TE,heap1) |- frames frames_conform_to FT;
qed by FRAMES_CONFORM_TO,TE_wf,heap0_conforms,heap1_bigger,
frames_conform_in_heap0,val_conforms-mono-lemma [TE,heap0/heap0,heap1/heap1]; // needs long search

// this says that object allocation preserves heap conformance
//
// A nasty but interesting proof.

// first prove initial creates values that conform
thm initial-values-conform
if TE |- vt wf_vartype vt_wf
then (TE,heap) |- initial(vt) wconforms_to vt goal;
consider st',dim st
+ vt = VT(st',dim)
- (TE,heap) |- initial(vt) rval_hastype vt g by WCONFORMS_TO,varty_widens_to-refl [vt,TE],goal;
have TE |- st' wf_simptype st_wf by vt_wf,WF_VARTYPE;
consider pt st vt = VT(PrimT(pt),0)
by g,initial,
rule cases on st_wf,st_wf,
rval_hastype__NullToClass [TE,heap],
rval_hastype__NullToInterface [TE,heap],
rval_hastype__NullToArray [TE,heap], ZERO_CASES [dim]; // case split helps determine when dim is zero - silly arith stuff!
qed by structural cases on pt,initial,
g,rval_hastype__Prim [TE,heap];

// allocation preserves weak conformance

thm wconforms_preserved-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
heap0 FPSUBFUN heap1 heap0_preserved
(TE,heap0) |- val wconforms_to vt val_wconforms_in_heap0
then (TE,heap1) |- val wconforms_to vt goal;
consider vt' st
+ (TE,heap0) |- val rval_hastype vt' val_types_in_heap0
+ TE |- vt' varty_widens_to vt widens
- ?vt''. (TE,heap1) |- val rval_hastype vt'' & TE |- vt'' varty_widens_to vt valgoal
by val_wconforms_in_heap0,goal,WCONFORMS_TO;
per cases
case val = RAddr(SOME(addr))
FPLOOKUP heap0 addr = SOME(OBJECT(fldvals,C)) lookup
vt' = VT(Class(C),0) ;
have FPLOOKUP heap1 addr = SOME(OBJECT(fldvals,C)) by heap0_preserved,lookup,FPSUBFUN [heap0/f1]
then qed by rval_hastype__AddrToClass [TE,heap1,fldvals,C,addr], valgoal [vt'], val_types_in_heap0, widens;
case val = RAddr(SOME(addr))
FPLOOKUP heap0 addr = SOME(ARRAY(VT(simpty,dim'),vec)) lookup
vt' = VT(simpty,ndim)
ndim = dim' + 1 ;
have FPLOOKUP heap1 addr = SOME(ARRAY(VT(simpty,dim'),vec)) by heap0_preserved,lookup,FPSUBFUN [heap0/f1];
then qed by rval_hastype__AddrToArray [TE,heap1,dim'/dim,vec,ndim/ndim,addr,simpty], valgoal [vt'], val_types_in_heap0, widens;
end by rule cases on val_types_in_heap0,
valgoal [vt'], widens,
rval_hastype__NullToClass [TE,heap1],
rval_hastype__NullToInterface [TE,heap1],
rval_hastype__NullToArray [TE,heap1],
rval_hastype__Prim [TE,heap1];

// allocation preserves value conformance

thm heap_leq-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
heap0 FPSUBFUN heap1 heap0_preserved
then TE |- heap0 heap_leq heap1 goal;
have !val vt. (TE,heap0) |- val wconforms_to vt -->
(TE,heap1) |- val wconforms_to vt wconforms_preserved
by wconforms_preserved-lemma [TE,heap0/heap0,heap1/heap1],TE_wf,
heap0_conforms,heap0_preserved,FPSUBFUN [heap0/f1];
consider addr,val_ty st
+ addr ::: FPDOM(heap0) addr_in_heap0
+ (TE,heap0) |- RAddr(SOME(addr)) val_conforms_to val_ty addr_conforms_in_heap0
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to val_ty
by goal,;
per cases
case
(TE,heap0) |- RAddr(SOME(addr)) wconforms_to val_ty addr_wconforms_in_heap0
FPLOOKUP heap0 addr = SOME(OBJECT(fldvals,C)) lookup
(TE,heap0) |- fldvals fldvals_wconform_to C fields_wconform_in_heap0;
have FPLOOKUP heap1 addr = SOME(OBJECT(fldvals,C)) lookup_in_heap1 by heap0_preserved,lookup,FPSUBFUN [heap0/f1];
have (TE,heap1) |- RAddr(SOME(addr)) wconforms_to val_ty addr_wconforms_in_heap1
by wconforms_preserved,TE_wf, addr_wconforms_in_heap0;
have (TE,heap1) |- fldvals fldvals_wconform_to C fields_wconform_in_heap1
by wconforms_preserved, fields_wconform_in_heap0,FLDVALS_WCONFORM_TO;
qed by addr_wconforms_in_heap1,lookup_in_heap1,
val_conforms_to__Object [TE,heap1,addr,C,val_ty,fldvals],
fields_wconform_in_heap1;
case
(TE,heap0) |- RAddr(SOME(addr)) wconforms_to val_ty addr_wconforms_in_heap0
FPLOOKUP heap0 addr = SOME(ARRAY(vt',vec)) lookup
(TE,heap0) |- vec els_wconform_to vt' els_wconform_in_heap0;
have FPLOOKUP heap1 addr = SOME(ARRAY(vt',vec)) lookup_in_heap1 by heap0_preserved,lookup,FPSUBFUN [heap0/f1];
have (TE,heap1) |- RAddr(SOME(addr)) wconforms_to val_ty addr_wconforms_in_heap1
by wconforms_preserved,TE_wf, addr_wconforms_in_heap0;
have (TE,heap1) |- vec els_wconform_to vt' els_wconform_in_heap1
by wconforms_preserved [vt'], els_wconform_in_heap0, ELS_WCONFORM_TO
qed by addr_wconforms_in_heap1,lookup_in_heap1,
val_conforms_to__Array [TE,heap1,addr,vec],
els_wconform_in_heap1;
end by hard rule cases on addr_conforms_in_heap0;

// if conformance is valid at all new addresses, then
// allocation results in a conformant heap.

thm alloc-conforms-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
heap0 FPSUBFUN heap1
!addr. addr ::: FPDOM(heap1) & ~(addr ::: FPDOM(heap0)) --> (TE,heap1) |- addr addr_conforms alloced_conform
then TE |- heap1 heap_conforms goal;
// now show the new heap is narrower, in the sense that it preserves value conformance
have TE |- heap0 heap_leq heap1 heap1_larger
by heap_leq-lemma [TE,heap0/heap0,heap1/heap1],
TE_wf,heap0_conforms;
// now show that the new heap conforms at every address. Only need to consider old
// addresses in detail because we know newly allocated things conform.
per cases
// The object case. Old objects conform because they are identical in the new heap.
case
+ addr ::: FPDOM(heap0) addr_in_heap0
+ FPLOOKUP heap1 addr = SOME(OBJECT(fldvals,C)) lookup
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to VT(Class(C),0);
have FPLOOKUP heap0 addr = SOME(OBJECT(fldvals,C)) lookup_in_heap0
by FPSUBFUN_REVERSE [addr,heap0/f1,heap1/f2,OBJECT(fldvals,C)/y'], lookup, addr_in_heap0,IN_FPDOM;
have (TE,heap0) |- RAddr(SOME(addr)) val_conforms_to VT(Class(C),0) addr_conforms_in_heap0
by HEAP_CONFORMS__rule [heap0,addr,TE],ADDR_CONFORMS__object [heap0,addr,C], addr_in_heap0, lookup_in_heap0, heap0_conforms;
then qed by addr_in_heap0,val_conforms-mono-lemma [heap0/heap0,heap1/heap1],TE_wf,heap1_larger,heap0_conforms;
// The array case. Old Arrays conform because they are identical in the new heap.
case
+ addr ::: FPDOM(heap0) addr_in_heap0
+ FPLOOKUP heap1 addr = SOME(ARRAY(VT(st,dim),vec)) lookup
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to VT(st,dim+1);
have FPLOOKUP heap0 addr = SOME(ARRAY(VT(st,dim),vec)) lookup_in_heap0
by FPSUBFUN_REVERSE [addr,heap0/f1,heap1/f2,ARRAY(VT(st,dim),vec)/y'], lookup, addr_in_heap0,IN_FPDOM;
have (TE,heap0) |- RAddr(SOME(addr)) val_conforms_to VT(st,dim+1) addr_conforms_in_heap0
by HEAP_CONFORMS__rule [heap0,addr,TE],ADDR_CONFORMS__array [heap0,addr,st,dim,vec,TE], addr_in_heap0, lookup_in_heap0, heap0_conforms;
then qed by addr_in_heap0,val_conforms-mono-lemma,TE_wf,heap1_larger,heap0_conforms;
end by hard goal,HEAP_CONFORMS,ADDR_CONFORMS,heap1_larger,alloced_conform;

// object allocation preserves conformance.

thm object-alloc-conforms-lemma
if TE wf_tyenv TE_wf
flds = PGRAPH {fspec | FDecs(TE,C1)(fspec)} flds
!fld vt. (fld,vt) :: FDecs(TE,C1) --> TE |- vt wf_vartype fields_wf
fldvals1 = initial o_p flds fldvals1
heapobj1 = OBJECT(fldvals1,C1) heapobj
sAlloc(heap0,heapobj1) = (heap1,addr1) alloc
TE |- heap0 heap_conforms heap0_conforms
then TE |- heap1 heap_conforms &
TE |- heap0 heap_leq heap1 goal;
// show all original addresses are preserved
have heap0 FPSUBFUN heap1 heap0_preserved
by hard FPSUBFUN_ELIM,alloc,sAlloc,freshi [FPDOM heap0],IN_FPDOM; // actually easy - I just need to fix higher-order case splitting
// now show the new heap is narrower, in the sense that it preserves value conformance
have TE |- heap0 heap_leq heap1 heap1_larger
by heap_leq-lemma [TE,heap0/heap0,heap1/heap1],heap0_preserved,TE_wf,heap0_conforms;
// the new address conforms
have !addr. addr ::: FPDOM(heap1) & ~(addr ::: FPDOM(heap0)) --> (TE,heap1) |- addr addr_conforms alloced_conform
proof then sts (TE,heap1) |- addr1 addr_conforms
by EXCLUDED_MIDDLE [addr = addr1], IN_ELIM,PDOM,alloc,sAlloc,freshi [FPDOM heap0]; // easy with splitting
have FPLOOKUP heap1 addr1 = SOME(heapobj1) [rw,auto] by heapobj,sAlloc,alloc;
have (TE,heap1) |- RAddr(SOME(addr1)) rval_hastype VT(Class(C1),0) weak
by rval_hastype__AddrToClass [fldvals1,heap1,TE,addr1,C1],heapobj;
then have (TE,heap1) |- RAddr(SOME(addr1)) wconforms_to VT(Class(C1),0) weak
by WCONFORMS_TO,varty_widens_to-refl;
have (TE,heap1) |- fldvals1 fldvals_wconform_to C1
proof then consider idx,vt' st
+ FDecs(TE,C1)(idx,vt') idx_is_field
- ?val. fldvals1 idx = SOME(val) &
(TE,heap1) |- val wconforms_to vt' by FLDVALS_WCONFORM_TO;
have TE |- vt' wf_vartype vt_wf by fields_wf,idx_is_field,IN_ELIM;
have flds(idx) = SOME(vt')
by object-fields-form-graph [TE],TE_wf,
PGRAPH_SOME [{fspec | FDecs(TE,C1)(fspec)}/R],flds,idx_is_field,GSPEC_ELIM;
then have fldvals1(idx) = SOME(initial vt') [rw,auto] by fldvals1;
qed by initial-values-conform,vt_wf; ;
then qed by val_conforms_to__Object [fldvals1,C1,heap1,TE,VT(Class(C1),0),addr1],
weak,ADDR_CONFORMS,heapobj; ;
qed by heap1_larger,alloc-conforms-lemma [heap1/heap1,heap0/heap0,TE],
alloced_conform,heap0_conforms,heap0_preserved,TE_wf;

// this says that array allocation preserves heap conformance
//
// A nasty but interesting proof.

// allocation is a doubly recursive process - once for the number of dimensions
// and once for each dimension. Hence we have a double induction. We prove
// that the key properties are preserved throughout this process.
thm array-alloc-conforms-lemma // subsumes array-alloc-types-lemma
if TE wf_tyenv TE_wf
TE |- VT(st,ext) wf_vartype st_wf
(val1,heap1) = val_alloc(heap0,st,dims,ext) alloc
TE |- heap0 heap_conforms heap0_conforms
then heap0 FPSUBFUN heap1 &
TE |- heap1 heap_conforms &
(TE,heap1) |- val1 wconforms_to VT(st,LEN(dims)+ext) goal;
proceed by structural induction on dims with dims,heap0,heap1,val1 variable;
case NIL dims = [] ;
qed by heap_leq-refl,heap0_conforms,alloc,val_alloc,initial-values-conform [TE,VT(st,ext)],st_wf;
case CONS
dims = dim × dims' dims
!heap0 heap1 val1.
(val1,heap1) = val_alloc(heap0,st,dims',ext) &
TE |- heap0 heap_conforms -->
heap0 FPSUBFUN heap1 &
TE |- heap1 heap_conforms &
(TE,heap1) |- val1 wconforms_to VT(st,LEN(dims')+ext) ihyp;
let arr_ty = VT(st,LEN(dims)+ext) arr_ty;
let alloc_step = LAM(vec,heap). let (val,heap') = val_alloc(heap,st,dims',ext) in (val×vec,heap') alloc_step;
consider heapB,heapobjB,vecB,addrB st
(vecB,heapB) = REPEATN dim ([],heap0) alloc_step repeat_alloc_step
heapobjB = ARRAY(VT(st,LEN(dims')+ext),vecB) heapobj
(heap1,addrB) = sAlloc(heapB,heapobjB) alloc2
val1 = RAddr(SOME(addrB)) val1 by val_alloc,alloc,alloc_step;
have heap0 FPSUBFUN heapB heap0_preservedB
TE |- heapB heap_conforms heapB_conforms
(TE,heapB) |- vecB els_wconform_to VT(st,LEN(dims')+ext) vecB_wconforms
proof proceed by structural induction on dim with dim,vecB,heapB variable
discarding alloc,goal,alloc2,heapobj,val1,arr_ty,dims;
// Discussion about nested inductions:
// val1, heap1, dims and addrB are irrelevant for the inner induction, but
// are not logically held constant. They appear in
// the induction condition below in the conditions as equalities, but that's all.
// These conditions are automatically eliminated. Without rigid induction
// case specs these would not be needed.
//
// The situation is actually worse than I thought - the whole original goal appears
// all over again in the induction hypothesis. Blah.
//
// Problem solved! Allow explicit mention of goals/facts to discard/include
case 0 dim = 0 ;
have (vecB,heapB) = ([],heap0) by repeat_alloc_step;
qed by heap_leq-refl,heap0_conforms,ELS_WCONFORM_TO;
case SUC
dim = SUC k
!vecB heapB.
(vecB,heapB) = REPEATN k ([],heap0) alloc_step
-->
heap0 FPSUBFUN heapB &
TE |- heapB heap_conforms &
(TE,heapB) |- vecB els_wconform_to VT(st,LEN(dims')+ext) repeat-ihyp;
let elem_ty = VT(st,LEN(dims')+ext) ;
consider heapA,vecA,valB st
(vecA,heapA) = REPEATN k ([],heap0) alloc_step inductive-computation
(valB,heapB) = val_alloc(heapA,st,dims',ext) step-computation
vecB = valB × vecA by alloc_step,repeat_alloc_step;
have heap0 FPSUBFUN heapA t1
TE |- heapA heap_conforms heapA_conforms
(TE,heapA) |- vecA els_wconform_to elem_ty t6
by repeat-ihyp [heapA/heapB,vecA/vecB],inductive-computation;
have heapA FPSUBFUN heapB u1
TE |- heapB heap_conforms heapB_conforms
(TE,heapB) |- valB wconforms_to elem_ty u6
by ihyp [heapA/heap0,heapB/heap1,valB/val1],step-computation,heapA_conforms;
have heap0 FPSUBFUN heapB v1
by u1,t1,FPSUBFUN_ELIM;
have (TE,heapB) |- vecB els_wconform_to elem_ty v2
by t6,u6,EL_CONS_ELIM,ELS_WCONFORM_TO,
wconforms_preserved-lemma [TE,heapA/heap0,heapB/heap1,elem_ty],
TE_wf,heapA_conforms,u1,arith11; // needs arith - yuck
qed by v1,v2,heapB_conforms;
end; ;
let elem_ty = VT(st,LEN(dims')+ext) ;
have heapB FPSUBFUN heap1 heapB_preserved
by hard alloc2,sAlloc,freshi [FPDOM heapB],IN_FPDOM,FPSUBFUN_ELIM; // actually easy - I just need to fix higher-order case splitting
then have heap0 FPSUBFUN heap1 heap0_preserved
by heap0_preservedB,FPSUBFUN_ELIM; // actually easy - I just need to fix higher-order case splitting
// now show the new heap is narrower, in the sense that it preserves value conformance
have TE |- heap0 heap_leq heap1 heap1_larger
by heap_leq-lemma [TE,heap0/heap0,heap1/heap1],heap0_preserved,TE_wf,heap0_conforms;
// now show the new heap is narrower, in the sense that it preserves value conformance
have TE |- heapB heap_leq heap1 heap1_largerB
by heap_leq-lemma [TE,heapB/heap0,heap1/heap1],heapB_preserved,TE_wf,heapB_conforms;
have FPLOOKUP heap1 addrB = SOME(heapobjB) [rw,auto] by heapobj,sAlloc,alloc2;
// this is needed for the induction invariant
have (TE,heap1) |- val1 wconforms_to arr_ty val1_wconforms
by val1,WCONFORMS_TO__derive [val1,TE,heap1,arr_ty/val_ty],
rval_hastype__AddrToArray [TE,LEN(dims)+ext/ndim,LEN(dims')+ext/dim,vecB,heap1,addrB,st],heapobj;
have !addr. addr ::: FPDOM(heap1) & ~(addr ::: FPDOM(heap0)) --> (TE,heap1) |- addr addr_conforms alloced_conform
proof then consider addr st
+ addr ::: FPDOM(heap1) a1
+ ~(addr ::: FPDOM(heap0)) a0
- (TE,heap1) |- addr addr_conforms g;
then per cases
case + addr ::: FPDOM(heapB) addr_in_B;
per cases
case + FPLOOKUP heap1 addr = SOME(OBJECT(fldvals,C)) lookup
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to VT(Class(C),0) goal;
have FPLOOKUP heapB addr = SOME(OBJECT(fldvals,C))
by addr_in_B,IN_FPDOM,heapB_preserved,
FPSUBFUN_REVERSE [OBJECT(fldvals,C)/y',addr,heapB/f1,heap1/f2],lookup;
then have (TE,heapB) |- RAddr(SOME(addr)) val_conforms_to VT(Class(C),0)
by heapB_conforms,HEAP_CONFORMS__rule [heapB,TE,addr],
ADDR_CONFORMS__object [heapB,TE,addr],addr_in_B;
then qed by val_conforms-mono-lemma [heapB/heap0,heap1/heap1],TE_wf,heap1_largerB,heapB_conforms;
case + FPLOOKUP heap1 addr = SOME(ARRAY(VT(stD,dimD),vec)) lookup
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to VT(stD,dimD+1);
have FPLOOKUP heapB addr = SOME(ARRAY(VT(stD,dimD),vec))
by lookup,addr_in_B,IN_FPDOM,heapB_preserved,
FPSUBFUN_REVERSE [ARRAY(VT(stD,dimD),vec)/y',addr,heapB/f1,heap1/f2];
then have (TE,heapB) |- RAddr(SOME(addr)) val_conforms_to VT(stD,dimD+1)
by heapB_conforms,HEAP_CONFORMS__rule [heapB,TE],
ADDR_CONFORMS__array [TE,addr,heapB],addr_in_B;
then qed by val_conforms-mono-lemma [heapB/heap0,heap1/heap1,TE],TE_wf,heap1_largerB,heapB_conforms;
end by ADDR_CONFORMS,g;
case addr = addrB ;
have !val vt. (TE,heapB) |- val wconforms_to vt -->
(TE,heap1) |- val wconforms_to vt wconforms_preserved
by wconforms_preserved-lemma [TE,heapB/heap0,heap1/heap1],
TE_wf,heapB_conforms,heapB_preserved,FPSUBFUN [heapB/f1,heap1/f2];
have (TE,heap1) |- vecB els_wconform_to elem_ty
by vecB_wconforms,wconforms_preserved,ELS_WCONFORM_TO;
then have (TE,heap1) |- val1 val_conforms_to arr_ty
by val_conforms_to__Array [addr,TE,heap1,arr_ty/vt,VT(st,LEN(dims')+ext)/vt',vecB],
val1_wconforms,val1,heapobj;
then qed by heapobj,ADDR_CONFORMS,val1;
end by IN_FPDOM,alloc2,sAlloc; ;
have TE |- heap1 heap_conforms heap1_conforms
by heap1_larger,alloc-conforms-lemma [TE,heap0/heap0,heap1/heap1],
alloced_conform,heap0_conforms,heap0_preserved,TE_wf;
qed by heap0_preserved,heap1_conforms,val1_wconforms;
end;

// this says that stack assignment preserves frames conformance

thm stack-assign-conforms-lemma
if TE wf_tyenv TE_wf
TE |- heap heap_conforms heap_conforms
(TE,heap) |- frames0 frames_conform_to FT frames0_conform
fidx < LEN(FT)
(EL(fidx)(FT))(id) = SOME(vty) lookup
(TE,heap) |- val rval_hastype ety val_types
TE |- ety varty_widens_to vty widens
frames1 = sSetStackVar(frames0,fidx,id,val)
then (TE,heap) |- frames1 frames_conform_to FT
have LEN frames0 = LEN FT [rw,auto] by FRAMES_CONFORM_TO,frames0_conform;
have (TE,heap) |- val val_conforms_to ety
by val_types,heap_conforms,TE_wf,conforms-from-typing [TE,heap];
then have (TE,heap) |- val val_conforms_to vty
by val_conforms-trans,TE_wf,widens;
then qed by FRAMES_CONFORM_TO,
frames0_conform,sGetStackVar,sSetStackVar,lookup,EL_REPLACE_ELIM; // needs long search??

// this says that array assignment preserves heap conformance. The runtime
// check implies that the value assigned into the array weakly conforms,
// which is all we need.

thm field-assign-conforms-lemma
if TE wf_tyenv TE_wf
TE |- heap0 heap_conforms heap0_conforms
FPLOOKUP heap0 taddr = SOME(OBJECT(fldvals,C')) lookup
((C,fld),vty) :: FDecs(TE,C') visible
(TE,heap0) |- sval rval_hastype ety sval_types
TE |- ety varty_widens_to vty sval_fits
heap1 = sFieldSet(heap0,taddr,C,fld,sval) heap1
then TE |- heap1 heap_conforms &
TE |- heap0 heap_leq heap1 g;
have !val vt. (TE,heap0) |- val wconforms_to vt -->
(TE,heap1) |- val wconforms_to vt wconforms_preserved
proof then consider val,val_ty st
(TE,heap0) |- val rval_hastype val_ty val_types
- (TE,heap1) |- val rval_hastype val_ty g
byWCONFORMS_TO
per cases
case FPLOOKUP heap0 addr = SOME(OBJECT(fldvals2,C2)) [rw,auto]
val = RAddr(SOME(addr))
val_ty = VT(Class(C2),0) ;
qed by rval_hastype__AddrToClass [TE,heap1,C2,addr],
lookup,EXCLUDED_MIDDLE [addr = taddr],heap1,sFieldSet,PAPPLY_ELIM,hoFieldSet;
case FPLOOKUP heap0 addr = SOME(ARRAY(VT(simpty2,dim2),vec2)) [rw,auto]
ndim2 = dim2+1
val = RAddr(SOME(addr))
val_ty = VT(simpty2,ndim2) ;
qed by rval_hastype__AddrToArray [TE,heap1,addr],
rval_hastype__AddrToArray [TE,heap1,taddr],
EXCLUDED_MIDDLE [addr = taddr],lookup,COND_RATOR,COND_RAND,heap1,sFieldSet,hoFieldSet,PAPPLY_ELIM;
end by rule cases on val_types,EXCLUDED_MIDDLE [addr = taddr],g,heap1,sFieldSet,PAPPLY_ELIM,hoFieldSet,
rval_hastype__NullToClass [TE,heap1],
rval_hastype__NullToInterface [TE,heap1],
rval_hastype__NullToArray [TE,heap1],
rval_hastype__Prim [TE,heap1]; ;
// the value assigned to the field weakly conforms
have (TE,heap1) |- sval wconforms_to vty sval_wconforms
by WCONFORMS_TO,sval_fits,sval_types,TE_wf,wconforms_preserved;
have TE |- heap0 heap_leq heap1 heap1_larger
proof then consider addr,val_ty st
addr ::: FPDOM(heap0)
(TE,heap0) |- RAddr(SOME(addr)) val_conforms_to val_ty addr_conforms
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to val_ty by ;
per cases
case addr <> taddr
(TE,heap0) |- RAddr(SOME(addr)) wconforms_to val_ty addr_wconforms
FPLOOKUP heap1 addr = SOME(OBJECT(fldvals2,C2)) [rw,auto]
(TE,heap0) |- fldvals2 fldvals_wconform_to C2 fields_wconform;
have (TE,heap1) |- fldvals2 fldvals_wconform_to C2 by wconforms_preserved,FLDVALS_WCONFORM_TO,fields_wconform;
then qed by val_conforms_to__Object [TE,addr,heap1,fldvals2,C2,val_ty],
wconforms_preserved,addr_wconforms;
case addr = taddr
(TE,heap0) |- RAddr(SOME(addr)) wconforms_to val_ty addr_wconforms
vec' = REPLACE(idx)(tvec)(sval)
FPLOOKUP heap1 addr = SOME(OBJECT(fldvals',C')) [auto,rw]
fldvals' = fldvals <+ ((C,fld),sval) fldvals2
(TE,heap0) |- fldvals fldvals_wconform_to C' fldvals_conform;
have (TE,heap1) |- fldvals' fldvals_wconform_to C'
proof then consider Cf,f,vt' st
+ FDecs(TE,C')((Cf,f),vt')
- ?val. fldvals' (Cf,f) = SOME(val) &
(TE,heap1) |- val wconforms_to vt' by FLDVALS_WCONFORM_TO;
per cases
case (Cf,f) = (C,fld) ;
have vty = vt' by visible,IN_ELIM, object-fields-unique [TE],PFORALL_SPLIT,TE_wf;
qed by fldvals_conform,FLDVALS_WCONFORM_TO,wconforms_preserved,
sval_wconforms,fldvals2;
case (Cf,f) <> (C,fld) ;
qed by fldvals_conform,FLDVALS_WCONFORM_TO__rule [TE,fldvals,(Cf,f),vt'],
wconforms_preserved,fldvals2;
end; ;
then qed by val_conforms_to__Object [TE,addr,heap1,fldvals',val_ty/vt],
addr_wconforms,wconforms_preserved;
case (TE,heap0) |- RAddr(SOME(addr)) wconforms_to val_ty addr_wconforms
FPLOOKUP heap0 addr = SOME(ARRAY(VT(simpty',dim'),vec')) [auto,rw]
(TE,heap0) |- vec' els_wconform_to VT(simpty',dim') vec_wconforms;
have FPLOOKUP heap1 addr = SOME(ARRAY(VT(simpty',dim'),vec')) [rw,auto] a by lookup,heap1,sFieldSet,hoFieldSet,PAPPLY_ELIM;
have (TE,heap1) |- vec' els_wconform_to VT(simpty',dim') by wconforms_preserved,ELS_WCONFORM_TO,vec_wconforms;
then qed by a,val_conforms_to__Array [TE,addr,heap1,VT(simpty',dim')/vt',vec',val_ty/vt],
addr_wconforms,wconforms_preserved;
end by rule cases on addr_conforms,lookup,EXCLUDED_MIDDLE [addr = taddr],heap1,sFieldSet,hoFieldSet,PAPPLY_ELIM; ;
per cases
case
FPLOOKUP heap1 addr = SOME(OBJECT(fldvals2,C2)) lookup2
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to VT(Class(C2),0);
consider fldvals'' st FPLOOKUP heap0 addr = SOME(OBJECT(fldvals'',C2)) in0 by lookup,lookup2,heap1,sFieldSet,hoFieldSet,PAPPLY_ELIM;
then have (TE,heap0) |- RAddr(SOME(addr)) val_conforms_to VT(Class(C2),0)
by HEAP_CONFORMS__rule [addr,TE,heap0],heap0_conforms,
ADDR_CONFORMS__object [TE,addr,heap0,fldvals'',C2],IN_FPDOM;
then qed by PEXISTS_SPLIT,heap1_larger,HEAP_LEQ__rule [TE,heap0/heap0,heap1/heap1,VT(Class(C2),0),addr],in0,IN_FPDOM;
case
FPLOOKUP heap1 addr = SOME(ARRAY(VT(st',dim'),vec')) lookup2
- (TE,heap1) |- RAddr(SOME(addr)) val_conforms_to VT(st',dim'+1);
have FPLOOKUP heap0 addr = SOME(ARRAY(VT(st',dim'),vec')) in0 by lookup,lookup2,heap1,sFieldSet,hoFieldSet,PAPPLY_ELIM;
then have (TE,heap0) |- RAddr(SOME(addr)) val_conforms_to VT(st',dim'+1)
by HEAP_CONFORMS__rule [addr,TE,heap0],heap0_conforms,
ADDR_CONFORMS__array [TE,addr,heap0,st',vec',dim'],IN_FPDOM;
then qed by heap1_larger,HEAP_LEQ__rule [TE,heap0/heap0,heap1/heap1,VT(st',dim'+1),addr],in0,IN_FPDOM;
end by g,HEAP_CONFORMS,heap1_larger,ADDR_CONFORMS,
|