metavar x, y, z, f ::= indexvar i, j, k, n, m ::= grammar td :: top_level_decl_ ::= {{com top-level declaration}} | tx f <( tparam0 , .. , tparamk )> ( tx0 x0 , .. , txm xm ) { s0 .. sn } :: :: func_decl | typedef x <( tparam0 , .. , tparamk )> = tx ; :: :: typedef tparam :: type_parameter_ ::= {{com type parameter}} | tx x :: :: constexpr | x : y0 + .. + yn :: :: type_variable s :: stmt_ ::= {{com statement}} | if ( e ) s :: :: if_then {{com Desugared}} | while ( e ) s :: :: while {{com Desugared}} | for ( eOrVDecls ; eOrNothing ; eOrNothing' ) s :: :: for {{com Desugared}} | tx vdecl0 , .. , vdecln ; :: :: multi_vdecls {{com partly desugared}} | ; :: :: empty {{com Desugared}} | if ( e ) s else s' :: :: if | do s while ( e ) ; :: :: do_while | switch ( e ) { sc0 : sblock0 .. scn : sblockn } :: :: switch | break ; :: :: break | continue ; :: :: continue | fallthrough ; :: :: fallthrough | return e ; :: :: return | return ; :: :: return_void | trap ; :: :: trap | { blockAnnot s0 .. sn } :: :: block | e ; :: :: effectful_expr | tval x : sid ; :: :: resolved_vdecl {{com post-monomorphisation variable declaration}} | Loop ( s , s' ) :: :: loop_construct {{com Special, only during execution}} | Cases ( s0 , .. , sn ) :: :: cases_construct {{com Special, only during execution}} | Join ( s ) :: :: join_construct {{com Special, only during execution}} sc :: switch_case_ ::= | case rval :: :: case | default :: :: default sblock :: switch_block_ ::= | s0 .. sn :: :: statements vdecl :: variable_declaration_ ::= | x :: :: uninitialized | x = e :: :: initialized eOrVDecls :: expr_or_vdecl_ ::= | e :: :: expr | tx vdecl0 , .. , vdecln :: :: vdecl | :: :: nothing eOrNothing :: expr_or_nothing_ ::= | e :: :: expr | :: :: nothing blockAnnot :: block_annotation_ ::= | R :: :: exec_env {{tex _{[[R]]} }} | :: :: nothing e :: expr_ ::= {{com expression}} | ( e ) :: :: parens | e , e' :: :: comma | e || e' :: :: or {{tex [[e]]\:{||}\:[[e']]}} | e && e' :: :: and {{tex [[e]]\:{\&\&}\:[[e']]}} | e0 ? e1 : e2 :: :: ternary {{tex [[e0]]\:{?}\:[[e1]]:[[e2]]}} | ! e :: :: not {{tex \:![[e]]}} | e == e' :: :: equals_operator | e != e' :: :: not_equals_operator {{com Desugared}} {{tex [[e]]\;!\mkern-\thickmuskip=[[e']]}} | e = e' :: :: assignment | x :: :: variable_name | * e :: :: ptr_deref | & e :: :: address_taking | @ e :: :: array_reference_making | e [ e' stride ] :: :: array_index {{tex [[e]] [ [[e']] ]_{[[stride]]} }} | x <( targ0 , .. , targm )> ( e0 , .. , en ) :: :: call | fid <( rv0 , .. , rvn )> ( e0 , .. , em ) :: :: resolved_call {{com post-monomorphisation, calls are resolved, and pure type arguments are gone}} | val :: :: val {{com only during exec, except literals}} | Call s :: :: call_construct {{com only during exec}} | JoinExpr ( e ) :: :: join_construct {{com only during exec}} val :: val_ ::= | rval :: :: rval | LVal ( addr ) :: :: lval stride :: stride_ ::= | k :: :: stride {{com stride annotation added during monomorphisation}} | :: :: nothing {{com no stride annotation}} addr :: addr_ ::= | addr + i * k :: :: add_multiple_stride | sid :: :: sid targ :: type_argument_ ::= {{com type argument}} | x :: :: ident {{com either a type or a constexpr}} | tx :: :: type {{com a type that is not just an identifier}} | x . y :: :: enum_value {{com a field of an enum, for a constexpr type parameter}} G {{tex \Gamma}} , Gglobal {{tex \Gamma_{global} }} :: env_ ::= {{com typing environment}} | G [ x -> envMapping ] :: :: update {{com $\Gamma$ with the mapping for x replaced by envMapping}} | { x0 -> envMapping0 , .. , xn -> envMappingn } :: :: set envMapping :: env_mapping_ ::= | t :: :: var {{com $x$ is of type $\tau$}} | SyntaxTypedef ( <( tparam0 , .. , tparamn )> -> tx ) :: :: typedef_syntax {{com resolved before local typing}} | Typedef ( <( tparam0 , .. , tparamn )> -> tval ) :: :: typedef | Func { sig0 , .. , sign } :: :: func {{com $x$ is a function whose signatures are $sig0$ to $sign$}} | SyntaxFunc { sigx0 , .. , sigxn } :: :: func_syntax | Nothing :: :: nothing {{tex \emptyset}} {{com to remove $x$ from $\Gamma$}} | Protocol namedSigs :: :: protocol % TODO: I now believe that this is wrong, and that protocols should live in their own namespace. % Otherwise it is impossible to refer to a protocol as both a type variable (aka Self) and as a constraint on other type variables, when defining its signatures namedSigs :: named_signatures_ ::= | { x0 -> sig0 , .. , xn -> sign } :: :: explicit | U namedSigs0 .. namedSigsn :: :: union sigx :: signature_syntax_ ::= | <( tparamAnon0 , .. , tparamAnonm )> ( tx0 , .. , txn ) -> tx :: :: sigx sig :: signature_ ::= | <( tparamAnon0 , .. , tparamAnonm )> ( tval0 , .. , tvaln ) -> tval :: :: sig % TODO: fix these tparamAnanon, they are clearly wrong. tparamAnon :: tparam_anon_ ::= | t :: :: constexpr | { y0 , .. , yn } :: :: tvar B :: behaviour_ ::= {{com statement behaviour}} | { b0 , .. , bn } :: :: set | B + B' :: :: union {{tex [[B]] \cup [[B']]}} | B \ B' :: :: difference {{tex [[B]] \backslash [[B']]}} | U B0 .. Bn :: :: big_union | ( B ) :: :: parens b :: single_behaviour_ ::= | Return t :: :: return | Break :: :: break | Continue :: :: continue | Fallthrough :: :: fallthrough | Nothing :: :: Nothing t {{tex \tau}} :: type_ ::= {{com type}} | LVal ( tval ) :: :: lval {{com left value}} | tval :: :: tval tval {{tex {\tau^{val} } }} :: type_value_ ::= | Ptr ( tval ) :: :: ptr {{com pointer}} | Ref ( tval ) :: :: array_ref {{com array reference}} | [ tval ] :: :: array {{com array}} | bool :: :: bool | uint32 :: :: uint32 {{tex \textbf{uint32} }} | void :: :: void | TVar tid namedSigs :: :: tvar tx {{tex {\tau^{syntax} } }} :: type_syntactic_ ::= {{com syntactic type}} | x <( targ0 , .. , targm )> :: :: base | tx * addressSpace :: :: ptr | tx [] addressSpace :: :: array_ref | tx [ i ] :: :: array_fixed_size addressSpace :: address_space_ ::= | thread :: :: thread | threadgroup :: :: threadgroup | device :: :: device | constant :: :: constant tid :: type_identifier_ ::= rval, rv :: rval_ ::= | { x0 : rval0 ; .. ; xn : rvaln } :: :: struct | [ rval0 , .. , rvaln ] :: :: array | Ptr ( addr ) :: :: ptr | Ref ( addr , j ) :: :: ref {{com Reference to array of size j}} | null :: :: lit_null | true :: :: lit_true | false :: :: lit_false | uint :: :: lit_uint {{com unsigned integer literal}} | TrapValue :: :: trap {{com Special, represents a caught error (e.g. out-of-bounds access)}} | Void :: :: void {{com Special, the return value of a void function call}} R {{tex \rho}}, Rout {{tex \rho_{out} }} :: exec_env_ ::= {{com execution environment}} | R [ x0 -> val0 , .. , xn -> valn ] :: :: update_with_vals | Empty :: :: empty {{tex \emptyset}} E :: memory_event_ ::= {{com memory event}} | :: :: nothing | addr <- rval :: :: store {{com store}} | addr -> rval :: :: load {{com load}} | Sequence ( E0 , .. , En ) :: :: multiple_events fid :: function_identifier_ ::= S :: stack_event_ ::= {{com stack event}} | :: :: nothing | push ( rval ) :: :: push | pop ( ) :: :: pop terminals :: terminals_ ::= | U :: :: big_union {{tex \bigcup}} | |- :: :: vdash {{tex \vdash}} | <- :: :: load {{tex \leftarrow}} | -> :: :: mapsto {{tex \mapsto}} | --> :: :: desugars {{tex \leadsto}} | <( :: :: generic_open {{tex {<} }} % For removing extraneous spaces around '<' and '>' when they are used in that position. | )> :: :: generic_close {{tex {>} }} | in :: :: in {{tex \in}} | \/ :: :: math_or {{tex \vee}} | /\ :: :: math_and {{tex \wedge}} | <= :: :: math_lesser_equal {{tex \leq}} | >= :: :: math_greater_equal {{tex \geq}} formula :: formula_ ::= | judgement :: :: judgement | formula0 /\ .. /\ formulan :: :: several_formula | formula \/ formula' :: :: or | n > 0 :: :: int_positive | x -> envMapping in G :: :: env_mapping_exists | x not in G :: :: env_mapping_missing {{tex [[x]] \not\in [[G]]}} | G |- isIntegerOrEnum ( t ) :: :: is_integer | G |- sc0 .. scn fully covers t :: :: full_switch_coverage % TODO: make it explicit | s != s' :: :: stmt_not_eq {{tex [[s]] \neq [[s']]}} | b in B :: :: behaviour_in {{tex [[b]] \in [[B]]}} | b not in B :: :: behaviour_not_in {{tex [[b]] \not\in [[B]]}} | G = G' :: :: typing_env_eq | B = B' :: :: behaviour_eq | namedSigs = namedSigs' :: :: named_signature_eq | sig = sig' :: :: sig_eq | e = e' :: :: expr_eq | e != e' :: :: expr_neq {{tex [[e]] \neq [[e']]}} | exists i . formula :: :: exists_int | tid = makeFresh() :: :: make_fresh_tid | i = uint :: :: uint_to_indexvar | i <= n :: :: indexvar_leq | i >= n :: :: indexvar_geq | i < n :: :: indexvar_lesser | x -> val in R :: :: val_in_env | fid -> <( x0 , .. , xn )> ( y0 : addr0 , .. , ym : addrm ) { s0 .. sk } :: :: fid_resolving | E = E' :: :: event_eq | R = R' :: :: exec_env_eq | s = s' :: :: stmt_eq | rv not in sc0 .. scn :: :: rval_not_in_cases % TODO: fix typesetting | s = { sblock } :: :: block_from_switch_block | rv = Default ( tval ) :: :: default_value | s is a terminator :: :: stmt_is_terminator defns desugaring :: '' ::= defn s --> s' :: :: desugaring_stmt :: '' {{com Desugaring statements}} by ----------------------------- :: if_then if (e) s --> if (e) s else {} -------- :: empty_stmt ; --> {} -------------------------------------- :: while while (e) s --> if (e) do s while (e); -------------------------------------------------------------------------- :: for_empty_cond for (eOrVDecls ; ; eOrNothing) s --> for (eOrVDecls ; true ; eOrNothing) s --------------------------------------------------------------------------- :: for_empty_incr for (eOrVDecls ; e ; ) s --> for (eOrVDecls ; e ; null) s ------------------------------------------------ :: for_init_expr for (e ; e' ; e'') s --> {e; while(e') {s e'';}} ------------------------------------------------ :: for_init_empty for ( ; e' ; e'') s --> while(e') {s e'';} ------------------------------------------------------------------------------------------ :: for_init_vdecls for (tx vdecl0 , .. , vdecln ; e' ; e'') s --> {tx vdecl0 , .. , vdecln; while(e') {s e'';}} k > 0 -------------------------------------------------------------------------------------- :: multiple_vdecls { s0..sn tx vdecl0, vdecl1, .., vdeclk; s'0..s'm} --> {s0..sn tx vdecl0; tx vdecl1, .., vdeclk; s'0..s'm} ------------------------------------------------------------- :: initialized_vdecl { s0..sn tx x = e; s'0..s'm} --> {s0..sn tx x; x = e; s'0..s'm} % TODO: replace foo(e0,..,en) by foo<>(e0,..,en) % Both in expressions, and in top-level declarations % TODO: also desugar syntactic types that have an addressSpace as a prefix. % Also make it an error to have an addressSpace and neither array ref nor ptr. defn e --> e' :: :: desugaring_expr :: '' {{com Desugaring expressions}} by ----------------------- :: not_equals_operator e != e' --> ! (e == e') %defns %gather :: '' ::= %defn %G = gather ( td0 .. tdn ) :: :: gather :: '' by % % G = gather (td0..tdk) % x -> SyntaxFunc{sigx0, .., sigxi} in G % G' = G[x -> SyntaxFunc{sigx0, .., sigxi, <( )>(tx0, .., txn) -> tx}] % -------------------------------------------------------------------- :: func_overload % G' = gather(tx x <( )> (tx0 x0, .., txn xn) {s0 .. sm} td0..tdk) % % G = gather (td0..tdk) % x not in G % G' = G[x ->SyntaxFunc{<()>(tx0, .., txn) -> tx}] % ---------------------------------------------------------------- :: func_no_overload % G' = gather(tx x <( )> (tx0 x0, .., txn xn) {s0 .. sm} td0..tdk) % % G = gather (td0..tdk) % x not in G % G' = G[x -> SyntaxTypedef(<()> -> tx)] % ------------------------------------------ :: typedef % G' = gather(typedef x <()> = tx; td0..tdk) % % TODO: add rules for the other kinds of top-level declaration, as well as the base case for an empty file % % the base case needs to add typedefs for bool/uint32 at the very least defns reduce_type :: '' ::= defn G |- tx : tval :: :: syntax :: '' {{com Evaluating types}} {{tex [[G]] [[|-]] [[tx]] \Downarrow [[tval]]}} by x -> Typedef (<( )> -> tval) in G ------------------------------- :: basic_typedef G |- x <( )> : tval G |- tx : tval ------------------------- :: array G |- tx [i] : [tval] G |- tx : tval ---------------------------------------- :: ref G |- tx [] addressSpace : Ref(tval) G |- tx : tval --------------------------------------- :: ptr G |- tx * addressSpace : Ptr(tval) defns well_formed :: '' ::= defn G |- well_formed ( td ) :: :: top_level :: '' by G |- tx : tval G |- {s0..sn} : {Return tval} ---------------------------------- :: func_trivial G |- well_formed(tx f<()>() {s0..sn}) G |- tx0 : tval G[x0 -> LVal(tval)] |- well_formed (tx f<()>(tx1 x1, .., txm xm) {s0..sn}) -------------------------------------------------------------------- :: func_param G |- well_formed (tx f<()>(tx0 x0, tx1 x1, .., txm xm) {s0..sn}) G |- tx' : tval' G[x -> tval'] |- well_formed (tx f<(tparam0, .., tparamk)> (tx0 x0, .., txm xm) {s0..sn}) ---------------------------------------------------------------------------------------- :: func_constexpr G |- well_formed (tx f<(tx' x, tparam0, .., tparamk)> (tx0 x0, .., txm xm) {s0..sn}) y0 -> Protocol namedSigs0 in G /\ .. /\ yi -> Protocol namedSigsi in G namedSigs = U namedSigs0 .. namedSigsi tid = makeFresh() G[x -> TVar tid namedSigs] |- well_formed (tx f<(tparam0, .., tparamk)> (tx0 x0, .., txm xm) {s0..sn}) ------------------------------------------------------------------------------------------------------ :: func_tvar G |- well_formed (tx f<(x : y0 + .. + yi, tparam0, .., tparamk)> (tx0 x0, .., txm xm) {s0..sn}) % TODO: maybe add some check here that the different protocols are not incompatible. % Can we even have incompatible protocols? defns typing :: '' ::= defn G |- s : B :: :: typing_statement :: '' {{com Validating statements' behaviours}} by G |- e : bool G |- s : B G |- s' : B' ------------------------------ :: if G |- if (e) s else s' : B + B' G |- e : bool G |- s : B ---------------------------------------------------------- :: do_while G |- do s while (e); : (B \ {Break, Continue}) + {Nothing} % Note: we could make this rule a bit more precise in the cases where we know that s always return/trap/break.. but such a piece of code is almost certainly a bug. G |- e : t G |- isIntegerOrEnum(t) G |- sc0: t /\ .. /\ G |- scn: t G |- sc0 .. scn fully covers t G |- sblock0: B0 /\ .. /\ G |- sblockn: Bn Nothing not in B0 /\ .. /\ Nothing not in Bn B = U B0 .. Bn --------------------------------------------------------------------------- :: switch G |- switch (e) {sc0: sblock0 .. scn : sblockn } : B \ {Break, Fallthrough} --------------------- :: break G |- break; : {Break} --------------------------- :: continue G |- continue; : {Continue} --------------------------------- :: fallthrough G |- fallthrough; : {Fallthrough} G |- e : t --------------------------- :: return G |- return e; : {Return t} ----------------------------- :: return_void G |- return ; : {Return void} ----------------------- :: trap G |- trap; : {Return t} ------------------- :: empty_block G |- {} : {Nothing} G[x -> Nothing] |- tx : tval G[x -> LVal(tval)] |- {s0 .. sn} : B s0 != tx' x; /\../\ sn != tx' x; --------------------------------- :: variable_decl G |- {tx x; s0 .. sn} : B % Note: we remove x from the environment before reducing tx, to avoid a situation where x is required in the definition of tx, which gets rather weird with multiple declarations % (see email from 20/06/2018). % Note: there is a minor ambiguity between this rule and the next two, but it is harmless as the next two rules both fail in the next step % if they are applied where s is a variable declaration. % Note: the second premise prevents redeclaration of a variable in the same scope it was declared in. % Implemented naively it takes O(n**2) to check, but can easily be optimized. G |- s : B ------------ :: trivial_block G |- {s} : B G |- s : B G |- {s1 .. sn} : B' n > 0 Nothing in B -------------------------------------- :: block G |- {s s1 .. sn} : (B \ {Nothing}) + B' % Note: the last premise forbids trivially dead code. It is optional and could be removed with no consequences on the rest of the language. G |- e : t ------------------- :: expr G |- e; : {Nothing} defn G |- sc : t :: :: typing_switch_case :: '' by G |- rval : t --------------- :: case G |- case rval : t ---------------- :: default G |- default : t defn G |- sblock : B :: :: typing_switch_block :: '' by G |- { s0 .. sn } : B --------------------- :: switch_block G |- s0 .. sn : B defn G |- e : t :: :: typing_expr :: '' {{com Typing expressions}} by ------------------- :: null_lit_array_ref G |- null : Ref (tval) ------------------- :: null_lit_ptr G |- null : Ptr (tval) ---------------- :: literal_true G |- true : bool ----------------- :: literal_false G |- false : bool G |- e : t ------------ :: parens G |- (e) : t G |- e : t G |- e' : t' --------------- :: comma G |- e, e' : t' G |- e : bool G |- e' : bool ------------------- :: or G |- e || e' : bool G |- e : bool G |- e' : bool ------------------- :: and G |- e && e' : bool G |- e0 : bool G |- e1 : t G |- e2 : t --------------------- :: ternary G |- e0 ? e1 : e2 : t G |- e : bool -------------- :: not G |- !e : bool G |- e : LVal(tval) G |- e' : tval ----------------- :: assignment G |- e = e' : tval x -> t in G ----------- :: variable_name G |- x : t G |- e : LVal(tval) ---------------- :: lval_access G |- e : tval G |- e : LVal(tval) ---------------- :: address_taking G |- &e : Ptr(tval) % can the unary operator & be overloaded? % It seems that no G |- e : Ptr(tval) ----------------- :: ptr_deref G |- *e : LVal(tval) % can the unary operator * be overloaded? % It seems that no % Note: We currently do not have any special interaction between pointers and array references in these rules G |- e : LVal(tval) ---------------- :: take_ref_lval G |- @e : Ref(tval) % Note: in the execution rules, the behaviour depends on whether that LVal points to an array, but here we don't need to track it. G |- e : LVal([tval]) G |- e' : uint32 -------------------- :: array_index_lval G |- e[e'] : LVal(tval) G |- e : [tval] G |- e' : uint32 ---------------- :: array_index_rval G |- e[e'] : tval % There is a choice between applying array_index_lval and then lval_access, or lval_access and then array_index_rval. % It is not problematic, because the rules are confluent, so either choice leads to the same result. % TODO: should we refuse during validation the case where e' is a constant that falls out of the bounds of e ? % I think it should be an allowed behaviour but not required of the implementation. G |- e : Ref(tval) G |- e' : uint32 -------------------- :: array_ref_index G |- e[e'] : LVal(tval) G |- e0 : tval0 /\../\ G |- en : tvaln x -> Func{sig0, .., sigk} in G exists i . sigi = <( )> (tval0, .., tvaln) -> tval -------------------------------------------------- :: call_no_targ G |- x <( )> (e0, .., en) : tval defns exec :: '' ::= defn R |- e -> e' ; E ; S :: :: exec_expr :: '' {{com Small-step reduction on expressions}} {{tex [[R]] \vdash [[e]] \xrightarrow[ [[S]] ]{[[E]]} [[e']]}} by ------------------------------------------- :: and_true R |- true && e -> JoinExpr(e) ;; push(true) --------------------------- :: and_false R |- false && e -> false ;; R |- e0 -> e0' ; E ; S ---------------------------------- :: and_reduce R |- e0 && e1 -> e0' && e1 ; E ; S e != LVal(addr) R |- e -> e' ; E ; S --------------------------------------- :: join_expr_reduce R |- JoinExpr(e) -> JoinExpr(e'); E ; S ---------------------------------- :: join_expr_elim R |- JoinExpr(val) -> val; ; pop() ------------------------- :: or_true R |- true || e -> true ;; --------------------------------------------- :: or_false R |- false || e -> JoinExpr(e) ;; push(false) R |- e0 -> e0' ; E ; S ---------------------------------- :: or_reduce R |- e0 || e1 -> e0' || e1 ; E ; S ------------------------------------------------- :: ternary_true R |- true ? e1 : e2 -> JoinExpr(e1) ;; push(true) --------------------------------------------------- :: ternary_false R |- false ? e1 : e2 -> JoinExpr(e2) ;; push(false) R |- e0 -> e0' ; E ; S ------------------------------------------ :: ternary_reduce R |- e0 ? e1 : e2 -> e0' ? e1 : e2 ; E ; S ---------------------- :: comma_next R |- rval, e1 -> e1 ;; R |- e0 -> e0' ; E ; S ------------------------------ :: comma_reduce R |- e0, e1 -> e0', e1 ; E ; S ------------------ :: parens_exec R |- ( e ) -> e ;; ----------------------- :: not_true R |- ! true -> false ;; ----------------------- :: not_false R |- ! false -> true ;; --------------------------------- :: deref_ptr R |- * Ptr(addr) -> LVal(addr) ;; R |- e -> e' ; E ; S ------------------------ :: deref_reduce R |- * e -> * e' ; E ; S e != LVal(addr) R |- e -> e' ; E ; S ------------------------ :: take_ptr_reduce R |- & e -> & e' ; E ; S -------------------------------- :: take_ptr_lval R |- & LVal(addr) -> Ptr(addr);; % TODO: add the '@' operator. It is tricky, because the monomorphisation pass must annotate it with the resulting size of the reference, basically by rerunning the type checking. % TODO: do we want to eliminate the next few rules, and instead put them into the definition of operator[] on the default types? % it would allow for nicer interaction with protocols. e0 != LVal(addr) R |- e0 -> e0' ; E ; S -------------------------------- :: array_left_reduce R |- e0 [e1] -> e0' [e1] ; E ; S e0 = LVal(addr) \/ e0 = [rval0, .., rvaln] \/ e0 = Ref(addr, j) R |- e1 -> e1' ; E ; S --------------------------------------------------------------- :: array_right_reduce R |- e0 [e1] -> e0 [e1'] ; E ; S i = uint i <= n ------------------------------------------ :: array_lit_access R |- [rval0, .., rvaln] [uint] -> rvali ;; i = uint ----------------------------------------------- :: array_unchecked R |- LVal(addr) [uint k] -> LVal(addr + i*k) ;; % TODO: we will have to add a check either here, or in the local typechecking phase, or in the monomorphisation phase. i = uint i < j ------------------------------------------------- :: array_ref_access_valid R |- Ref(addr, j) [uint k] -> LVal(addr + i*k) ;; i = uint i >= j ------------------------------------------ :: array_ref_access_invalid R |- Ref(addr, j) [uint k] -> TrapValue ;; x -> val in R ---------------- :: environment_access R |- x -> val ;; --------------------------------------- :: load R |- LVal(addr) -> rval ; addr -> rval; e0 != LVal(addr) R |- e0 -> e0' ; E ; S -------------------------------- :: assign_left_reduce R |- e0 = e1 -> e0' = e1 ; E ; S R |- e1 -> e1' ; E ; S ------------------------------------------------ :: assign_right_reduce R |- LVal(addr) = e1 -> LVal(addr) = e1' ; E ; S ---------------------------------------------- :: assign_execute R |- LVal(addr) = rval -> rval ; addr <- rval; R |- e -> e' ; E ; S ---------------------------------------------------------------------------------------------------------------------- :: call_reduce R |- fid<(rv0, .., rvn)>(rv'0, .., rv'm, e, e0, .., ek) -> fid<(rv0, .., rvn)>(rv'0, .., rv'm, e', e0, .., ek) ; E ; S fid -> <(x0, .., xn)>(y0:addr0, .., ym:addrm) {s0 .. sk} E = Sequence(addr0 <- rv'0, .., addrm <- rv'm) R' = R[x0 -> rv0, .., xn -> rvn] R'' = R'[y0 -> LVal(addr0), .., ym -> LVal(addrm)] -------------------------------------------------------------------- :: call_resolve R |- fid<(rv0, .., rvn)>(rv'0, .., rv'm) -> Call {R'' s0 .. sk} ; E; R |- s -> s' ; E ; S ------------------------------ :: call_construct_reduce R |- Call s -> Call s' ; E ; S --------------------------------- :: call_return R |- Call return rval; -> rval ;; ---------------------------- :: call_return_void R |- Call return; -> Void ;; ------------------------- :: call_end_function R |- Call {R'} -> Void ;; defn R |- s -> s' ; E ; S :: :: exec_stmt :: '' {{com Small-step reduction on statements}} {{tex [[R]] \vdash [[s]] \xrightarrow[ [[S]] ]{[[E]]} [[s']]}} by ----------------------------- :: block_annotate R |- {s0..sn} -> {R s0..sn};; R |- s -> s' ; E ; S --------------------------------------------- :: block_reduce Rout |- {R s s1..sn} -> {R s' s1..sn} ; E ; S --------------------------------------- :: block_next_stmt Rout |- {R {R'} s1..sn} -> {R s1..sn};; s = break; \/ s = continue; \/ s = fallthrough; \/ s = return rval; \/ s = return; \/ s = trap; ----------------------------------------------------------------------------------------------- :: block_terminator Rout |- {R s s1..sn} -> s;; R' = R[x -> LVal(sid)] rv = Default(tval) ------------------------------------------------------------ :: block_vdecl Rout |- {R tval x : sid; s1..sn} -> {R' s1..sn} ; sid <- rv; R |- e -> e' ; E ; S ---------------------- :: effectful_expr_reduce R |- e; -> e'; ; E ; S ------------------- :: effectful_expr_elim R |- rval; -> {} ;; R |- e -> e' ; E ; S ------------------------------------ :: return_reduce R |- return e; -> return e'; ; E ; S R |- e -> e' ; E ; S -------------------------------------------------- :: if_reduce R |- if (e) s else s' -> if (e') s else s' ; E ; S ------------------------------------------------- :: if_true R |- if (true) s else s' -> Join(s) ;; push(true) ---------------------------------------------------- :: if_false R |- if (false) s else s' -> Join(s') ;; push(false) R |- s -> s' ; E ; S -------------------------------- :: join_reduce R |- Join(s) -> Join(s') ; E ; S s = {R'} \/ s is a terminator ----------------------------- :: join_elim R |- Join(s) -> s ;; pop() -------------------------------------------------------------- :: do_while_loop R |- do s while(e); -> Loop(s, if(e) do s while(e); else {});; R |- s1 -> s1' ; E ; S ------------------------------------------ :: loop_reduce R |- Loop(s1, s2) -> Loop(s1', s2) ; E ; S ----------------------------- :: loop_break R |- Loop(break;, s2) -> {};; s1 = {R'} \/ s1 = continue; --------------------------- :: loop_next_iteration R |- Loop(s1, s2) -> s2;; s1 = fallthrough; \/ s1 = return; \/ s1 = return rval; \/ s1 = trap; -------------------------------------------------------------------- :: loop_other_terminator R |- Loop(s1, s2) -> s1;; R |- e -> e' ; E ; S ---------------------------------------------------------------------------------------------- :: switch_reduce R |- switch(e) {sc0:sblock0 .. scn:sblockn} -> switch(e') {sc0:sblock0 .. scn:sblockn} ; E ; S %TODO: the next two rules don't fit on the page. I should find a way to compact them. Maybe with the notation? s = {sblock} /\ s0 = {sblock'0} /\ .. /\ sm = {sblock'm} ------------------------------------------------------------------------------------------------------------------------------- :: switch_case_found R |- switch(rv) {sc0:sblock0 .. scn:sblockn case rv: sblock sc'0:sblock'0 .. sc'm:sblock'm} -> Cases(s, s0, .., sm) ;; push(rv) rv not in sc0 .. scn rv not in sc'0 .. sc'm s = {sblock} /\ s0 = {sblock'0} /\ .. /\ sm = {sblock'm} ------------------------------------------------------------------------------------------------------------------------------- :: switch_default R |- switch(rv) {sc0:sblock0 .. scn:sblockn default: sblock sc'0:sblock'0 .. sc'm:sblock'm} -> Cases(s, s0, .., sm) ;; push(rv) R |- s -> s' ; E ; S ------------------------------------------------------ :: cases_reduce R |- Cases(s, s0, .., sn) -> Cases(s', s0, .., sn) ; E ; S ----------------------------------------------------------- :: cases_fallthrough R |- Cases(fallthrough;, s0, .., sn) -> Cases(s0, .., sn);; --------------------------------------------- :: cases_break R |- Cases(break;, s0, .., sn) -> {} ;; pop() s = continue; \/ s = return; \/ s = return rval; \/ s = trap; ------------------------------------------------------------- :: cases_other_terminator R |- Cases(s, s0, .., sn) -> s ;; pop()