Require Import String stringmap.
Require Export type_system_decidable expression_eval error.
Local Open Scope string_scope.
Local Open Scope expr_scope.
Local Open Scope ctype_scope.
Local Open Scope list_scope.
Inductive cint_rank :
Set :=
|
CCharRank |
CShortRank |
CIntRank |
CLongRank |
CLongLongRank |
CPtrRank.
Inductive cint_type :=
CIntType {
csign :
option signedness;
crank :
cint_rank }.
Inductive climit :
Set :=
|
CESizeOf :
climit
|
CEAlignOf :
climit
|
CEOffsetOf :
string →
climit
|
CEMin :
climit
|
CEMax :
climit
|
CEBits :
climit.
Inductive cexpr :
Set :=
|
CEVar :
string →
cexpr
|
CEConst :
cint_type →
Z →
cexpr
|
CEConstString :
list Z →
cexpr
|
CELimit :
ctype →
climit →
cexpr
|
CEAddrOf :
cexpr →
cexpr
|
CEDeref :
cexpr →
cexpr
|
CEAssign :
assign →
cexpr →
cexpr →
cexpr
|
CECall :
cexpr →
list cexpr →
cexpr
|
CEAbort :
cexpr
|
CEAlloc :
ctype →
cexpr →
cexpr
|
CEFree :
cexpr →
cexpr
|
CEUnOp :
unop →
cexpr →
cexpr
|
CEBinOp :
binop →
cexpr →
cexpr →
cexpr
|
CEIf :
cexpr →
cexpr →
cexpr →
cexpr
|
CEComma :
cexpr →
cexpr →
cexpr
|
CEAnd :
cexpr →
cexpr →
cexpr
|
CEOr :
cexpr →
cexpr →
cexpr
|
CECast :
ctype →
cinit →
cexpr
|
CEField :
cexpr →
string →
cexpr
with cinit :
Set :=
|
CSingleInit :
cexpr →
cinit
|
CCompoundInit :
list (
list (
string +
cexpr) *
cinit) →
cinit
with ctype :
Set :=
|
CTVoid :
ctype
|
CTDef :
string →
ctype
|
CTEnum :
string →
ctype
|
CTInt :
cint_type →
ctype
|
CTPtr :
ctype →
ctype
|
CTArray :
ctype →
cexpr →
ctype
|
CTCompound :
compound_kind →
string →
ctype
|
CTFun :
list (
option string *
ctype) →
ctype →
ctype
|
CTTypeOf :
cexpr →
ctype.
Instance maybe_CEConstString :
Maybe CEConstString := λ
ce,
match ce with CEConstString zs =>
Some zs |
_ =>
None end.
Instance maybe_CTFun :
Maybe2 CTFun := λ
cτ,
match cτ
with CTFun cτ
s cτ =>
Some (
cτ
s,
cτ) |
_ =>
None end.
Instance maybe_CSingleInit :
Maybe CSingleInit := λ
ci,
match ci with CSingleInit ce =>
Some ce |
_ =>
None end.
Inductive cstorage :=
StaticStorage |
ExternStorage |
AutoStorage.
Instance cstorage_eq_dec (
sto1 sto2 :
cstorage) :
Decision (
sto1 =
sto2).
Proof.
solve_decision. Defined.
Inductive cstmt :
Set :=
|
CSDo :
cexpr →
cstmt
|
CSSkip :
cstmt
|
CSGoto :
string →
cstmt
|
CSBreak :
cstmt
|
CSContinue :
cstmt
|
CSReturn :
option cexpr →
cstmt
|
CSCase :
cexpr →
cstmt →
cstmt
|
CSDefault :
cstmt →
cstmt
|
CSScope :
cstmt →
cstmt
|
CSLocal :
list cstorage →
string →
ctype →
option cinit →
cstmt →
cstmt
|
CSTypeDef :
string →
ctype →
cstmt →
cstmt
|
CSComp :
cstmt →
cstmt →
cstmt
|
CSLabel :
string →
cstmt →
cstmt
|
CSWhile :
cexpr →
cstmt →
cstmt
|
CSFor :
cexpr →
cexpr →
cexpr →
cstmt →
cstmt
|
CSDoWhile :
cstmt →
cexpr →
cstmt
|
CSIf :
cexpr →
cstmt →
cstmt →
cstmt
|
CSSwitch :
cexpr →
cstmt →
cstmt.
Inductive decl :
Set :=
|
CompoundDecl :
compound_kind →
list (
string *
ctype) →
decl
|
EnumDecl :
cint_type →
list (
string *
option cexpr) →
decl
|
TypeDefDecl :
ctype →
decl
|
GlobDecl :
list cstorage →
ctype →
option cinit →
decl
|
FunDecl :
list cstorage →
ctype →
cstmt →
decl.
Inductive type_decl (
K :
Set) :
Set :=
|
Compound :
compound_kind →
list (
string *
type K) →
type_decl K
|
Enum :
int_type K →
type_decl K.
Arguments Compound {
_}
_ _.
Arguments Enum {
_}
_.
Instance maybe_Compound {
K} :
Maybe2 (@
Compound K) := λ
t,
match t with Compound c xs =>
Some (
c,
xs) |
_ =>
None end.
Instance maybe_Enum {
K} :
Maybe (@
Enum K) := λ
t,
match t with Enum τ
i =>
Some τ
i |
_ =>
None end.
Inductive global_decl (
K :
Set):
Set :=
|
Global :
cstorage →
index →
type K →
bool →
global_decl K
|
Fun:
cstorage →
list (
type K) →
type K →
option (
stmt K) →
global_decl K
|
GlobalTypeDef :
ptr_type K →
global_decl K
|
EnumVal :
int_type K →
Z →
global_decl K.
Arguments Global {
_}
_ _ _ _.
Arguments Fun {
_}
_ _ _ _.
Arguments GlobalTypeDef {
_}
_.
Arguments EnumVal {
_}
_ _.
Instance maybe_Fun {
K} :
Maybe4 (@
Fun K) := λ
d,
match d with Fun sto τ
s τ
ms =>
Some (
sto,τ
s,τ,
ms) |
_ =>
None end.
Instance maybe_GlobalTypeDef {
K} :
Maybe (@
GlobalTypeDef K) := λ
d,
match d with GlobalTypeDef τ =>
Some τ |
_ =>
None end.
Inductive local_decl (
K :
Set) :
Set :=
|
Extern : (
index *
type K +
list (
type K) *
type K) →
local_decl K
|
Local :
type K →
local_decl K
|
TypeDef :
ptr_type K →
local_decl K.
Arguments Extern {
_}
_.
Arguments Local {
_}
_.
Arguments TypeDef {
_}
_.
Instance maybe_TypeDef {
K} :
Maybe (@
TypeDef K) := λ
d,
match d with TypeDef τ =>
Some τ |
_ =>
None end.
None delimits scopes
Notation local_env K := (
list (
option (
string *
local_decl K))).
Record frontend_state (
K :
Set) :
Set :=
FState {
to_compounds :
tagmap (
type_decl K);
to_env :
env K;
to_mem :
mem K;
to_globals :
stringmap (
global_decl K)
}.
Arguments FState {
_}
_ _ _ _.
Arguments to_compounds {
_}
_.
Arguments to_env {
_}
_.
Arguments to_mem {
_}
_.
Arguments to_globals {
_}
_.
Local Notation M := (
error (
frontend_state _)
string).
Section frontend.
Context `{
Env K}.
Local Notation M := (
error (
frontend_state K)
string).
Global Instance empty_frontend_state :
Empty (
frontend_state K) :=
FState ∅ ∅ ∅ ∅.
Definition to_funenv (
S :
frontend_state K) :
funenv K :=
omap (λ
d, '(
_,
_,
ms) ←
maybe4 Fun d;
ms) (
to_globals S).
Definition incomplete_fun_decls (
S :
frontend_state K) :
funset :=
dom _ (
env_f (
to_env S)) ∖
dom _ (
to_funenv S).
Definition extern_global_decls (
S :
frontend_state K) :
stringset :=
mapset.mapset_dom_with (λ
d,
match d with Global ExternStorage _ _ _ =>
true |
_ =>
false end)
(
to_globals S).
Definition lookup_compound (
t :
tag) (
x :
string) :
M (
nat *
type K) :=
Γ
n ←
gets to_compounds;
d ←
error_of_option (Γ
n !!
t)
("
struct/
union `" +:+
t +:+ "`
undeclared");
'(
_,
xτ
s) ←
error_of_option (
maybe2 Compound d)
("
struct/
union `" +:+
t +:+ "`
instead of enum expected");
'(
i,
xτ) ←
error_of_option (
list_find ((
x =) ∘
fst)
xτ
s)
("
struct/
union `" +:+
t +:+ "`
does not have index `" +:+
x +:+ "`");
mret (
i,
xτ.2).
Fixpoint local_fresh (
x :
string) (Δ
l :
local_env K) :
bool :=
match Δ
l with
| [] |
None ::
_ =>
true
|
Some (
y,
_) :: Δ
l =>
if decide (
x =
y)
then false else local_fresh x Δ
l
end.
Fixpoint lookup_local_var (Δ
l :
local_env K)
(
x :
string) (
i :
nat) :
option (
expr K *
lrtype K) :=
match Δ
l with
| [] =>
None
|
Some (
y,
Extern (
inl (
o,τ))) :: Δ
l =>
if decide (
x =
y)
then Some (% (
Ptr (
addr_top o τ)),
inl (
TType τ))
else lookup_local_var Δ
l x i
|
Some (
y,
Extern (
inr (τ
s,τ))) :: Δ
l =>
if decide (
x =
y)
then Some (% (
FunPtr x τ
s τ),
inl (τ
s ~> τ))
else lookup_local_var Δ
l x i
|
Some (
y,
Local τ) :: Δ
l =>
if decide (
x =
y)
then Some (
var i,
inl (
TType τ))
else lookup_local_var Δ
l x (
S i)
|
Some (
y,
TypeDef _) :: Δ
l =>
if decide (
x =
y)
then None else lookup_local_var Δ
l x i
|
None :: Δ
l =>
lookup_local_var Δ
l x i
end.
Definition lookup_var (Δ
l :
local_env K)
(
x :
string) :
M (
expr K *
lrtype K) :=
match lookup_local_var Δ
l x 0
with
|
Some (
e,τ
lr) =>
mret (
e,τ
lr)
|
None =>
Δ
g ←
gets to_globals;
match Δ
g !!
x with
|
Some (
Global _ o τ
_) =>
mret (% (
Ptr (
addr_top o τ)),
inl (
TType τ))
|
Some (
EnumVal τ
i z) =>
mret (#
intV{τ
i}
z,
inr (
intT τ
i))
|
Some (
Fun _ τ
s τ
_) =>
mret (% (
FunPtr x τ
s τ),
inl (τ
s ~> τ))
|
_ =>
fail ("
variable `" +:+
x +:+ "`
not found")
end
end.
Fixpoint lookup_local_typedef (Δ
l :
local_env K)
(
x :
string) :
option (
ptr_type K) :=
match Δ
l with
| [] =>
None
|
Some (
y,
d) :: Δ
l =>
if decide (
x =
y)
then maybe TypeDef d else lookup_local_typedef Δ
l x
|
None :: Δ
l =>
lookup_local_typedef Δ
l x
end.
Definition lookup_typedef (Δ
l :
local_env K) (
x :
string) :
M (
ptr_type K) :=
match lookup_local_typedef Δ
l x with
|
Some τ
p =>
mret τ
p
|
None =>
Δ
g ←
gets to_globals;
error_of_option (Δ
g !!
x ≫=
maybe GlobalTypeDef)
("
typedef `" +:+
x +:+ "`
not found")
end.
Definition is_pseudo_NULL (
e :
expr K) :
bool :=
match e with #{
_} (
intV{
_} 0) =>
true |
_ =>
false end.
Definition to_R (
e :
expr K) (τ
lr :
lrtype K) :
M (
expr K *
type K) :=
match τ
lr with
|
inl (
TType τ) =>
match maybe2 TArray τ
with
|
Some (τ',
n) =>
mret (&(
e %>
RArray 0 τ'
n),
TType τ'.*)
|
None =>
Γ ←
gets to_env;
guard (
type_complete Γ τ)
with
"
l-
value conversion of expression with incomplete type";
mret (
load e, τ)
end
|
inl (τ
s ~> τ) =>
mret (&
e, (τ
s ~> τ).*)
|
inl TAny =>
fail "
l-
value conversion of expression with void type"
|
inr τ =>
mret (
e,τ)
end.
Definition to_R_NULL (σ :
type K)
(
e :
expr K) (τ
lr :
lrtype K) :
M (
expr K *
type K) :=
'(
e,τ) ←
to_R e τ
lr;
match σ
with
| σ
p.* =>
if is_pseudo_NULL e then mret (#
ptrV (
NULL σ
p), σ
p.*)
else mret (
e,τ)
|
_ =>
mret (
e,τ)
end.
Definition convert_ptrs (
eτ1
eτ2 :
expr K *
type K) :
option (
expr K *
expr K *
type K) :=
let (
e1,τ1) :=
eτ1
in let (
e2,τ2) :=
eτ2
in
match τ1, τ2
with
|
TAny.*,
TType _.* =>
Some (
e1,
cast{
TAny.*}
e2,
TAny.*)
|
TType _.*,
TAny.* =>
Some (
cast{
TAny.*}
e1,
e2,
TAny.*)
| τ
p1.*,
intT _ =>
guard (
is_pseudo_NULL e2);
Some (
e1, #
ptrV (
NULL τ
p1), τ
p1.*)
|
intT _, τ
p2.* =>
guard (
is_pseudo_NULL e1);
Some (#
ptrV (
NULL τ
p2),
e2, τ
p2.*)
|
_,
_ =>
None
end.
Definition to_if_expr (
e1 :
expr K)
(
eτ2
eτ3 :
expr K *
type K) :
option (
expr K *
lrtype K) :=
(* 1.) *) (
same types
let (
e2,τ2) :=
eτ2
in let (
e3,τ3) :=
eτ3
in
guard (τ2 = τ3);
Some (
if{
e1}
e2 else e3,
inr τ2)) ∪
(* 2.) *) (
common arithmetic conversions
let (
e2,τ2) :=
eτ2
in let (
e3,τ3) :=
eτ3
in
match τ2, τ3
with
|
intT τ
i2,
intT τ
i3 =>
let τ
i' :=
int_promote τ
i2 ∪
int_promote τ
i3 in
Some (
if{
e1}
cast{
intT τ
i'}
e2 else cast{
intT τ
i'}
e3,
inr (
intT τ
i'))
|
_,
_ =>
None
end) ∪
(* 3.) *) (
one side is NULL or void
'(
e2,
e3,τ) ←
convert_ptrs eτ2
eτ3;
Some (
if{
e1}
e2 else e3,
inr τ)).
Definition to_binop_expr (
op :
binop)
(
eτ1
eτ2 :
expr K *
type K) :
option (
expr K *
lrtype K) :=
(* 1.) *) (
let (
e1,τ1) :=
eτ1
in let (
e2,τ2) :=
eτ2
in
σ ←
binop_type_of op τ1 τ2;
Some (
e1 @{
op}
e2,
inr σ)) ∪
(* 2.) *) (
one side is NULL or void
guard (
op =
CompOp EqOp);
'(
e1,
e2,τ) ←
convert_ptrs eτ1
eτ2;
σ ←
binop_type_of (
CompOp EqOp) τ τ;
Some (
e1 @{
op}
e2,
inr σ)).
Definition int_const_types (
cτ
i :
cint_type) :
list (
int_type K) :=
let (
ms,
k) :=
cτ
i in
let s :=
from_option Signed ms in
match k with
|
CLongLongRank => [
IntType s longlong_rank]
|
CLongRank => [
IntType s long_rank;
IntType s longlong_rank]
|
_ => [
IntType s int_rank;
IntType s long_rank;
IntType s longlong_rank]
end.
Definition to_int_const (
x :
Z) :
list (
int_type K) →
option (
int_type K) :=
fix go τ
is :=
match τ
is with
| [] =>
None
| τ
i :: τ
is =>
if decide (
int_typed x τ
i)
then Some τ
i else go τ
is
end.
Definition to_inttype (
cτ
i :
cint_type) :
int_type K :=
let (
ms,
k) :=
cτ
i in
match k with
|
CCharRank =>
IntType (
from_option char_signedness ms)
char_rank
|
CShortRank =>
IntType (
from_option Signed ms)
short_rank
|
CIntRank =>
IntType (
from_option Signed ms)
int_rank
|
CLongRank =>
IntType (
from_option Signed ms)
long_rank
|
CLongLongRank =>
IntType (
from_option Signed ms)
longlong_rank
|
CPtrRank =>
IntType (
from_option Signed ms)
ptr_rank
end.
Definition to_string_const (
zs :
list Z) :
option (
val K *
nat) :=
guard (
Forall (λ
z,
int_typed z charT)
zs);
mret (
VArray (
intT charT)
(
VBase <$>
VInt charT <$> (
zs ++ [0%
Z])),
S (
length zs)).
Definition to_limit_const (τ :
type K) (
li :
climit) :
M (
Z *
int_type K) :=
match li with
|
CESizeOf =>
guard (τ ≠
voidT)
with "
sizeof of void type";
Γ ←
gets to_env;
let sz :=
size_of Γ τ
in
guard (
int_typed sz uptrT)
with "
argument of sizeof not in range";
mret (
Z.of_nat sz,
uptrT%
IT)
|
CEAlignOf =>
guard (τ ≠
voidT)
with "
alignof of void type";
Γ ←
gets to_env;
let sz :=
align_of Γ τ
in
guard (
int_typed sz uptrT)
with "
argument of sizeof not in range";
mret (
Z.of_nat sz,
uptrT%
IT)
|
CEOffsetOf x =>
'(
c,
t) ←
error_of_option (
maybe2 TCompound τ)
"
argument of offsetof not of struct type";
if decide (
c =
Union_kind)
then mret (0%
Z,
sptrT%
IT)
else
'(
i,
_) ←
lookup_compound t x;
Γ ←
gets to_env;
τ
s ←
error_of_option (Γ !!
t) "
argument of offsetof incomplete";
let off :=
offset_of Γ τ
s i in
guard (
int_typed off uptrT)
with "
argument of offsetof not in range";
mret (
Z.of_nat off,
uptrT%
IT)
|
CEMin =>
τ
i ←
error_of_option (
maybe (
TBase ∘
TInt) τ) "
min of non integer type";
mret (
int_lower τ
i, τ
i)
|
CEMax =>
τ
i ←
error_of_option (
maybe (
TBase ∘
TInt) τ) "
max of non integer type";
mret ((
int_upper τ
i - 1)%
Z, τ
i)
|
CEBits =>
τ
i ←
error_of_option (
maybe (
TBase ∘
TInt) τ) "
bits of non integer type";
mret (
Z.of_nat (
int_width τ
i), τ
i)
end.
Definition insert_object (γ :
perm) (
v :
val K) :
M index :=
m ←
gets to_mem; Γ ←
gets to_env;
let o :=
fresh (
dom _ m)
in
_ ←
modify (λ
S :
frontend_state K,
let (Γ
n,Γ,
m,Δ
g) :=
S in FState Γ
n Γ (
mem_alloc Γ
o false γ
v m) Δ
g);
mret o.
Definition update_object (
o :
index) (γ :
perm) (
v :
val K) :
M () :=
modify (λ
S :
frontend_state K,
let (Γ
n,Γ,
m,Δ
g) :=
S in FState Γ
n Γ (
mem_alloc Γ
o false γ
v m) Δ
g).
Definition insert_global_decl (
x :
string) (
d :
global_decl K) :
M () :=
modify (λ
S :
frontend_state K,
let (Γ
n,Γ,
m,Δ
g) :=
S in FState Γ
n Γ
m (<[
x:=
d]>Δ
g)).
Definition insert_fun (
f :
funname) (
sto :
cstorage)
(τ
s :
list (
type K)) (σ :
type K) (
ms :
option (
stmt K)) :
M () :=
modify (λ
S :
frontend_state K,
let (Γ
n,Γ,
m,Δ
g) :=
S in
FState Γ
n (<[
f:=(τ
s,σ)]>Γ)
m (<[(
f:
string):=
Fun sto τ
s σ
ms]>Δ
g)).
Definition insert_compound (
c :
compound_kind) (
t :
tag)
(
xτ
s :
list (
string *
type K)) :
M () :=
modify (λ
S :
frontend_state K,
let (Γ
n,Γ,
m,Δ
g) :=
S in
FState (<[
t:=
Compound c xτ
s]>Γ
n) (<[
t:=
xτ
s.*2]>Γ)
m Δ
g).
Definition insert_enum (
t :
tag) (τ
i :
int_type K) :
M () :=
modify (λ
S :
frontend_state K,
let (Γ
n,Γ,
m,Δ
g) :=
S in FState (<[
t:=
Enum τ
i]>Γ
n) Γ
m Δ
g).
Definition first_init_ref (Γ :
env K)
(τ :
type K) :
option (
ref K *
type K) :=
match τ
with
| τ.[
n] =>
Some ([
RArray 0 τ
n], τ)
|
structT t => τ ← Γ !!
t ≫= (!! 0);
Some ([
RStruct 0
t],τ)
|
unionT t => τ ← Γ !!
t ≫= (!! 0);
Some ([
RUnion 0
t false],τ)
|
_ =>
None
end.
Fixpoint next_init_ref (Γ :
env K)
(
r :
ref K) :
option (
ref K *
type K) :=
match r with
|
RArray i τ
n ::
r =>
if decide (
S i <
n)
then Some (
RArray (
S i) τ
n ::
r, τ)
else next_init_ref Γ
r
|
RStruct i t ::
r =>
match Γ !!
t ≫= (!! (
S i))
with
|
Some τ =>
Some (
RStruct (
S i)
t ::
r,τ) |
None =>
next_init_ref Γ
r
end
|
RUnion _ _ _ ::
r =>
next_init_ref Γ
r
|
_ =>
None
end.
Definition to_ref
(
to_expr :
cexpr →
M (
expr K *
lrtype K)) :
ref K →
type K →
list (
string +
cexpr) →
M (
ref K *
type K) :=
fix go r (τ :
type K)
xces {
struct xces} :=
match xces with
| [] =>
mret (
r,τ)
|
inl x ::
xces =>
'(
c,
t) ←
error_of_option (
maybe2 TCompound τ)
"
struct/
union initializer used for non-
compound type";
'(
i,τ) ←
lookup_compound t x;
let rs :=
match c with
|
Struct_kind =>
RStruct i t |
Union_kind =>
RUnion i t false
end in
go (
rs ::
r) τ
xces
|
inr ce ::
xces =>
'(σ,
n) ←
error_of_option (
maybe2 TArray τ)
"
array initializer used for non-
array type";
'(
e,
_) ←
to_expr ce;
guard (
is_pure e)
with
"
array initializer with non-
constant index";
Γ ←
gets to_env;
m ←
gets to_mem;
v ←
error_of_option (⟦
e ⟧ Γ []
m ≫=
maybe inr)
"
array initializer with undefined index";
'(
_,
x) ←
error_of_option (
maybe VBase v ≫=
maybe2 VInt)
"
array initializer with non-
integer index";
let i :=
Z.to_nat x in
guard (
i <
n)
with "
array initializer with index out of bounds";
go (
RArray i σ
n ::
r) σ
xces
end.
Definition to_compound_init
(
to_expr :
cexpr →
M (
expr K *
lrtype K))
(
to_init_expr :
type K →
cinit →
M (
expr K))
(τ :
type K) :
expr K →
list (
ref K) →
list (
list (
string +
cexpr) *
cinit) →
M (
expr K) :=
fix go e seen inits {
struct inits} :=
match inits with
| [] =>
mret e
| (
xces,
ci) ::
inits =>
Γ ←
gets to_env;
'(
r,σ) ←
if decide (
xces = [])
then error_of_option
match seen with
| [] =>
first_init_ref Γ τ |
r ::
_ =>
next_init_ref Γ
r
end "
excess elements in compound initializer"
else to_ref to_expr [] τ
xces;
guard (
Forall (
r ⊥.)
seen)
with "
element initialized before";
e1 ←
to_init_expr σ
ci;
go (#[
r:=
e1]
e) (
r ::
seen)
inits
end.
Definition to_call_fun (
e :
expr K) (τ
lr :
lrtype K) :
option (
expr K *
list (
type K) *
type K) :=
match τ
lr with
|
inl (τ
s ~> σ) =>
Some (
e,τ
s,σ)
|
inl (
TType τ) =>
'(τ
s,σ) ←
maybe (
TBase ∘
TPtr) τ ≫=
maybe2 TFun;
Some (.*(
load e),τ
s,σ)
|
inl TAny =>
None
|
inr τ => '(τ
s,σ) ←
maybe (
TBase ∘
TPtr) τ ≫=
maybe2 TFun;
Some (.*
e,τ
s,σ)
end.
Definition to_call_args
(
to_expr :
cexpr →
M (
expr K *
lrtype K)) :
list cexpr →
list (
type K) →
M (
list (
expr K)) :=
fix go ces τ
s :=
match ces, τ
s with
| [], [] =>
mret []
|
ce ::
ces, τ :: τ
s =>
'(
e,σ) ←
to_expr ce ≫=
curry (
to_R_NULL τ);
Γ ←
gets to_env;
guard (
cast_typed σ τ)
with "
function applied to arguments of incorrect type";
(
cast{τ}
e ::) <$>
go ces τ
s
|
_,
_ =>
fail "
function applied to the wrong number of arguments"
end.
Definition convert_fun_arg_type (τ
p :
ptr_type K) :
option (
type K) :=
match τ
p with
|
TType (τ.[
_]) =>
Some (
TType τ.*)
|
TType τ =>
Some τ
| τ
s ~> τ =>
Some ((τ
s ~> τ).*)
|
TAny =>
None
end.
Definition convert_fun_ret_type (τ
p :
ptr_type K) :
option (
type K) :=
match τ
p with
|
TType τ =>
Some τ
|
TAny =>
Some voidT
|
_ =>
None
end.
Definition to_fun_type_args
(
to_ptr_type :
ctype →
M (
ptr_type K)) :
list (
option string *
ctype) →
M (
list (
option string *
type K)) :=
fix go cτ
s :=
match cτ
s with
| [] =>
mret []
| (
mx,
cτ) ::
cτ
s =>
τ
p ←
to_ptr_type cτ;
τ ←
error_of_option (
convert_fun_arg_type τ
p)
"
function type with argument of void type";
((
mx,τ) ::) <$>
go cτ
s
end.
Definition fun_empty_args (
xτ
s :
list (
option string *
ctype)) :
bool :=
match xτ
s with [(
None,
CTVoid)] =>
true |
_ =>
false end.
Definition to_fun_type (
to_ptr_type :
ctype →
M (
ptr_type K))
(
cτ
s :
list (
option string *
ctype)) (
cτ :
ctype) :
M (
list (
option string *
type K) *
type K) :=
τ
p ←
to_ptr_type cτ;
τ ←
error_of_option (
convert_fun_ret_type τ
p)
"
function type returning function or array";
if fun_empty_args cτ
s then mret ([],τ)
else
xτ
s ←
to_fun_type_args to_ptr_type cτ
s;
guard (
NoDup (
omap fst xτ
s))
with
"
function type with duplicate argument names";
mret (
xτ
s,τ).
Definition rhs_6_5_16_1p3_safe :
expr K →
bool :=
fix go e :=
match e with
|
load (
var _) =>
true
|
load (%{
_}
_) =>
true
|
load _ =>
false
| #{
_}
_ =>
true
| &
e =>
true
|
_ ::={
_}
_ =>
true
|
call _ @
_ =>
true
|
abort _ =>
true
|
e #> (
RArray _ _ _ |
RStruct _ _) =>
go e
|
e #> (
RUnion _ _ _) =>
false
|
alloc{
_}
_ =>
true
|
free e =>
true
| @{
_}
e =>
go e
|
e1 @{
_}
e2 =>
go e1 &&
go e2
|
if{
_}
e2 else e3 =>
go e2 &&
go e3
|
_,,
e2 =>
go e2
|
cast{
_}
e =>
go e
| #[
_:=
_ ]
_ =>
true
|
_ =>
false
end.
Definition to_field_ref_seg
(
c :
compound_kind) (
i :
nat) (
t :
tag) :
ref_seg K :=
match c with
|
Struct_kind =>
RStruct i t |
Union_kind =>
RUnion i t false
end.
Inductive to_type_kind :=
to_Type |
to_Ptr.
Definition to_type_type {
K} (
k :
to_type_kind) :=
match k with to_Type =>
type K |
to_Ptr =>
ptr_type K end.
Definition to_type_ret {
k} (τ :
type K) :
M (
to_type_type k) :=
match k with to_Type =>
mret τ |
to_Ptr =>
mret (
TType τ)
end.
End frontend.
Fixpoint to_expr `{
Env K} (Δ
l :
local_env K)
(
ce :
cexpr) :
M (
expr K *
lrtype K) :=
match ce return M (
expr K *
lrtype K)
with
|
CEVar x =>
lookup_var Δ
l x
|
CEConst cτ
i z =>
τ
i ←
error_of_option (
to_int_const z (
int_const_types cτ
i))
("
integer constant " +:+
pretty z +:+ "
too large");
mret (# (
intV{τ
i}
z),
inr (
intT τ
i))
|
CEConstString zs =>
'(
v,
n) ←
error_of_option (
to_string_const zs)
"
char of string constant out of range";
o ←
insert_object perm_readonly v;
mret (% (
Ptr (
addr_top o (
charT.[
n]))),
inl (
TType (
charT.[
n])))
|
CELimit cτ
li =>
τ ←
to_type to_Type Δ
l cτ;
'(
z,τ
i) ←
to_limit_const τ
li;
mret (#
intV{τ
i}
z,
inr (
intT τ
i))
|
CEDeref ce =>
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry to_R;
τ
p ←
error_of_option (
maybe (
TBase ∘
TPtr) τ)
"
dereferencing non-
pointer type";
mret (.*
e,
inl τ
p)
|
CEAddrOf ce =>
'(
e,τ
lr) ←
to_expr Δ
l ce;
τ
p ←
error_of_option (
maybe inl τ
lr) "
taking address of r-
value";
mret (&
e,
inr (τ
p.*))
|
CEAssign ass ce1 ce2 =>
'(
e1,τ
lr1) ←
to_expr Δ
l ce1;
τ1 ←
error_of_option (
maybe (
inl ∘
TType) τ
lr1) "
assigning to r-
value";
'(
e2,τ2) ←
to_expr Δ
l ce2 ≫=
curry (
to_R_NULL τ1);
Γ ←
gets to_env;
guard (
assign_typed τ1 τ2
ass)
with "
assignment cannot be typed";
let e1 :=
if decide (
ass =
Assign ∧ ¬
rhs_6_5_16_1p3_safe e2)
then freeze true e1 else e1 in
mret (
e1 ::={
ass}
e2,
inr τ1)
|
CECall ce ces =>
'(
e,τ
lr) ←
to_expr Δ
l ce;
'(
e,τ
s,σ) ←
error_of_option (
to_call_fun e τ
lr)
"
called object is not a function";
Γ ←
gets to_env;
guard (
type_complete Γ σ)
with "
function called with incomplete type";
es ←
to_call_args (
to_expr Δ
l)
ces τ
s;
mret (
call e @
es,
inr σ)
|
CEAbort =>
mret (
abort voidT,
inr voidT)
|
CEAlloc cτ
ce =>
τ ←
to_type to_Type Δ
l cτ;
guard (τ ≠
voidT)
with "
alloc of void type";
'(
e,τ
sz) ←
to_expr Δ
l ce ≫=
curry to_R;
_ ←
error_of_option (
maybe (
TBase ∘
TInt) τ
sz)
"
alloc applied to argument of non-
integer type";
mret (&(
alloc{τ}
e),
inr (
TType τ.*))
|
CEFree ce =>
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry to_R;
τ
p ←
error_of_option (
maybe (
TBase ∘
TPtr ∘
TType) τ)
"
free applied to argument of non-
pointer type";
Γ ←
gets to_env;
guard (
type_complete Γ τ
p)
with "
free applied to argument of incomplete pointer type";
mret (
free (.*
e),
inr voidT)
|
CEUnOp op ce =>
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry to_R;
σ ←
error_of_option (
unop_type_of op τ) "
unary operator cannot be typed";
mret (@{
op}
e,
inr σ)
|
CEBinOp op ce1 ce2 =>
eτ1 ←
to_expr Δ
l ce1 ≫=
curry to_R;
eτ2 ←
to_expr Δ
l ce2 ≫=
curry to_R;
error_of_option (
to_binop_expr op eτ1
eτ2)
"
binary operator cannot be typed"
|
CEIf ce1 ce2 ce3 =>
'(
e1,τ1) ←
to_expr Δ
l ce1 ≫=
curry to_R;
τ
b ←
error_of_option (
maybe TBase τ1)
"
conditional argument of if expression of non-
base type";
guard (τ
b ≠
TVoid)
with
"
conditional argument of if expression of void type";
eτ2 ←
to_expr Δ
l ce2 ≫=
curry to_R;
eτ3 ←
to_expr Δ
l ce3 ≫=
curry to_R;
error_of_option (
to_if_expr e1 eτ2
eτ3) "
if expression cannot be typed"
|
CEComma ce1 ce2 =>
'(
e1,τ1) ←
to_expr Δ
l ce1 ≫=
curry to_R;
'(
e2,τ2) ←
to_expr Δ
l ce2 ≫=
curry to_R;
mret (
cast{
voidT}
e1,,
e2,
inr τ2)
|
CEAnd ce1 ce2 =>
'(
e1,τ1) ←
to_expr Δ
l ce1 ≫=
curry to_R;
τ
b1 ←
error_of_option (
maybe TBase τ1)
"
first argument of &&
of non-
base type";
guard (τ
b1 ≠
TVoid)
with "
first argument of &&
of void type";
'(
e2,τ2) ←
to_expr Δ
l ce2 ≫=
curry to_R;
τ
b2 ←
error_of_option (
maybe TBase τ2)
"
second argument of &&
of non-
base type";
guard (τ
b2 ≠
TVoid)
with "
second argument of &&
of void type";
mret (
if{
e1}
if{
e2} #
intV{
sintT} 1
else #
intV{
sintT} 0
else #(
intV{
sintT} 0),
inr sintT)
|
CEOr ce1 ce2 =>
'(
e1,τ1) ←
to_expr Δ
l ce1 ≫=
curry to_R;
τ
b1 ←
error_of_option (
maybe TBase τ1)
"
first argument of ||
of non-
base type";
guard (τ
b1 ≠
TVoid)
with "
first argument of ||
of void type";
'(
e2,τ2) ←
to_expr Δ
l ce2 ≫=
curry to_R;
τ
b2 ←
error_of_option (
maybe TBase τ2)
"
second argument of ||
of non-
base type";
guard (τ
b2 ≠
TVoid)
with "
second argument of ||
of void type";
mret (
if{
e1} #
intV{
sintT} 0
else (
if{
e2} #
intV{
sintT} 1
else #(
intV{
sintT} 0)),
inr sintT)
|
CECast cσ
ci =>
σ ←
to_type to_Type Δ
l cσ;
guard (
maybe2 TArray σ =
None)
with "
array compound literal not supported";
guard (
maybe CSingleInit ci =
None ∨
maybe2 TCompound σ =
None)
with
"
cast to struct/
union not allowed";
e ←
to_init_expr Δ
l σ
ci;
mret (
e,
inr σ)
|
CEField ce x =>
'(
e,τ
lr) ←
to_expr Δ
l ce;
match τ
lr with
|
inl τ
p =>
'(
c,
t) ←
error_of_option (
maybe TType τ
p ≫=
maybe2 TCompound)
"
field operator applied to argument of non-
compound type";
'(
i,σ) ←
lookup_compound t x;
mret (
e %>
to_field_ref_seg c i t,
inl (
TType σ))
|
inr τ =>
'(
c,
t) ←
error_of_option (
maybe2 TCompound τ)
"
field operator applied to argument of non-
compound type";
'(
i,σ) ←
lookup_compound t x;
guard (
maybe2 TArray σ =
None)
with
"
indexing array field of r-
value struct/
union not supported";
mret (
e #>
to_field_ref_seg c i t,
inr σ)
end
end
with to_init_expr `{
Env K} (Δ
l :
local_env K)
(σ :
type K) (
ci :
cinit) :
M (
expr K) :=
match ci with
|
CSingleInit ce =>
match maybe CEConstString ce with
|
Some zs =>
'(
v,
n) ←
error_of_option (
to_string_const zs)
"
char of string initializer out of range";
if decide (σ =
type_of v)
then mret (#
v)
else
if decide (σ =
charT.*)
then
o ←
insert_object perm_readonly v;
mret (& ((%
Ptr (
addr_top o (
charT.[
n]))) %>
RArray 0
charT n))
else fail "
string initializer of wrong type or size"
|
None =>
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry (
to_R_NULL σ);
Γ ←
gets to_env;
guard (
cast_typed τ σ)
with "
cast or initializer cannot be typed";
mret (
cast{σ}
e)
end
|
CCompoundInit inits =>
Γ ←
gets to_env;
to_compound_init (
to_expr Δ
l) (
to_init_expr Δ
l) σ (#
val_0 Γ σ) []
inits
end
with to_type `{
Env K} (
k :
to_type_kind)
(Δ
l :
local_env K) (
cτ :
ctype) :
M (
to_type_type k) :=
match cτ
with
|
CTVoid =>
match k with to_Type =>
mret voidT |
to_Ptr =>
mret TAny end
|
CTDef x =>
τ
p ←
lookup_typedef Δ
l x;
match k return M (
to_type_type k)
with
|
to_Ptr =>
mret τ
p
|
to_Type =>
match τ
p with
|
TType τ =>
Γ ←
gets to_env;
guard (
type_complete Γ τ)
with "
complete typedef expected";
mret τ
|
TAny =>
mret voidT
|
_ =>
fail "
complete typedef expected"
end
end
|
CTEnum x =>
let t :
tag :=
x in
Γ
n ←
gets to_compounds;
τ
i ←
error_of_option (Γ
n !!
t ≫=
maybe Enum)
("
enum `" +:+
x +:+ "`
not found");
to_type_ret (
intT τ
i)
|
CTInt cτ
i =>
to_type_ret (
intT (
to_inttype cτ
i))
|
CTPtr cτ => τ
p ←
to_type to_Ptr Δ
l cτ;
to_type_ret (τ
p.*)
|
CTArray cτ
ce =>
τ ←
to_type to_Type Δ
l cτ;
guard (τ ≠
voidT)
with "
array with elements of void type";
'(
e,
_) ←
to_expr Δ
l ce;
guard (
is_pure e)
with "
array with non-
constant size expression";
Γ ←
gets to_env;
m ←
gets to_mem;
v ←
error_of_option (⟦
e ⟧ Γ []
m ≫=
maybe inr)
"
array with undefined size expression";
'(
_,
x) ←
error_of_option (
maybe VBase v ≫=
maybe2 VInt)
"
array with non-
integer size expression";
let n :=
Z.to_nat x in
guard (
n ≠ 0)
with "
array with negative or zero size expression";
to_type_ret (τ.[
n])
|
CTCompound c x =>
let t :
tag :=
x in
match k with
|
to_Ptr =>
mret (
compoundT{
c}
t)%
PT
|
to_Type =>
Γ ←
gets to_env;
guard (
is_Some (Γ !!
t))
with "
complete compound type expected";
mret (
compoundT{
c}
t)
end
|
CTFun cτ
s cτ =>
match k with
|
to_Type =>
fail "
complete type expected"
|
to_Ptr =>
'(
xτ
s,τ) ←
to_fun_type (
to_type to_Ptr Δ
l)
cτ
s cτ;
mret (
xτ
s.*2 ~> τ)
end
|
CTTypeOf ce =>
'(
_,τ
lr) ←
to_expr Δ
l ce;
match τ
lr with
|
inl (
TType τ) =>
Γ ←
gets to_env;
guard (
type_complete Γ τ)
with "
complete typeof expected";
to_type_ret τ
|
inr τ =>
to_type_ret τ
|
_ =>
fail "
typeof of function"
end
end.
Section frontend_more.
Context `{
Env K}.
Definition to_init_val (Δ
l :
local_env K)
(τ :
type K) (
ci :
cinit) :
M (
val K) :=
e ←
to_init_expr Δ
l τ
ci;
Γ ←
gets to_env;
m ←
gets to_mem;
guard (
is_pure e)
with "
initializer with non-
constant expression";
error_of_option (⟦
e ⟧ Γ []
m ≫=
maybe inr)
"
initializer with undefined expression".
Definition alloc_global (Δ
l :
local_env K) (
x :
string) (
sto :
cstorage)
(
cτ :
ctype) (
mci :
option cinit) :
M (
index *
type K +
list (
type K) *
type K) :=
τ
p ←
to_type to_Ptr Δ
l cτ;
Δ
g ←
gets to_globals;
match Δ
g !!
x with
|
Some (
Global sto'
o τ
init) =>
guard (τ
p =
TType τ)
with
("
global `" +:+
x +:+ "`
previously declared with different type");
guard (
sto =
ExternStorage ∨
sto =
sto'
∨
sto =
AutoStorage ∧
sto' =
ExternStorage)
with
("
global `" +:+
x +:+ "`
previously declared with different linkage");
match mci with
|
Some ci =>
guard (
sto ≠
ExternStorage)
with
("
global `" +:+
x +:+ "`
initialized and declared `
extern`");
guard (
init =
false)
with
("
global `" +:+
x +:+ "`
already initialized");
_ ←
insert_global_decl x (
Global sto o τ
true);
v ←
to_init_val Δ
l τ
ci;
_ ←
update_object o perm_full v;
mret (
inl (
o,τ))
|
None =>
mret (
inl (
o,τ))
end
|
Some (
Fun sto' τ
s σ
ms) =>
guard (τ
p = τ
s ~> σ)
with
("
function previously `" +:+
x +:+ "`
declared with different type");
guard (
sto =
ExternStorage ∨
sto =
sto'
∨
sto =
AutoStorage ∧
sto' =
ExternStorage)
with
("
function `" +:+
x +:+ "`
previously declared with different linkage");
guard (
mci =
None)
with ("
function `" +:+
x +:+ "`
with initializer");
_ ←
insert_fun x sto τ
s σ
ms;
mret (
inr (τ
s,σ))
|
Some (
GlobalTypeDef _) =>
fail ("
global `" +:+
x +:+ "`
previously declared as typedef")
|
Some (
EnumVal _ _) =>
fail ("
global `" +:+
x +:+ "`
previously declared as enum tag")
|
None =>
match τ
p with
| τ
s ~> σ =>
Γ ←
gets to_env;
guard (
mci =
None)
with ("
function `" +:+
x +:+ "`
with initializer");
_ ←
insert_fun x sto τ
s σ
None;
mret (
inr (τ
s,σ))
|
TType τ =>
Γ ←
gets to_env;
guard (
type_complete Γ τ)
with
("
global `" +:+
x +:+ "`
with incomplete type");
match mci with
|
Some ci =>
o ←
insert_object perm_full (
val_new Γ τ);
_ ←
insert_global_decl x (
Global sto o τ
true);
v ←
to_init_val Δ
l τ
ci;
_ ←
update_object o perm_full v;
mret (
inl (
o,τ))
|
None =>
o ←
insert_object perm_full (
val_0 Γ τ);
_ ←
insert_global_decl x (
Global sto o τ
false);
mret (
inl (
o,τ))
end
|
TAny =>
fail ("
global `" +:+
x +:+ "`
of void type")
end
end.
Definition alloc_static (Δ
l :
local_env K) (
x :
string) (
cτ :
ctype)
(
mci :
option cinit) :
M (
index *
type K) :=
τ ←
to_type to_Type Δ
l cτ;
guard (τ ≠
voidT)
with ("
static `" +:+
x +:+ "`
of void type");
Γ ←
gets to_env;
match mci with
|
Some ci =>
o ←
insert_object perm_full (
val_new Γ τ);
v ←
to_init_val (
Some (
x,
Extern (
inl (
o,τ))) :: Δ
l) τ
ci;
_ ←
update_object o perm_full v;
mret (
o, τ)
|
None => (,τ) <$>
insert_object perm_full (
val_0 Γ τ)
end.
Definition to_storage (
stos :
list cstorage) :
option cstorage :=
match stos with [] =>
Some AutoStorage | [
sto] =>
Some sto |
_ =>
None end.
Notation break_continue := (
option nat *
option nat)%
type.
Definition to_stmt (τ
ret :
type K) :
break_continue →
local_env K →
cstmt →
M (
stmt K *
rettype K) :=
fix go bc Δ
l cs {
struct cs} :=
match cs with
|
CSDo ce =>
'(
e,
_) ←
to_expr Δ
l ce ≫=
curry to_R;
mret (!(
cast{
voidT}
e), (
false,
None))
|
CSSkip =>
mret (
skip, (
false,
None))
|
CSGoto l =>
mret (
goto l, (
true,
None))
|
CSBreak =>
n ←
error_of_option (
bc.1) "
unbound break";
mret (
throw n, (
true,
None))
|
CSContinue =>
n ←
error_of_option (
bc.2) "
unbound break";
mret (
throw n, (
true,
None))
|
CSReturn (
Some ce) =>
guard (τ
ret ≠
voidT)
with
"
return with expression in function returning void";
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry (
to_R_NULL τ
ret);
Γ ←
gets to_env;
guard (
cast_typed τ τ
ret)
with "
return expression has incorrect type";
mret (
ret (
cast{τ
ret}
e), (
true,
Some τ
ret))
|
CSReturn None =>
guard (τ
ret =
voidT)
with "
return with no expression in non-
void function";
mret (
ret (#
voidV), (
true,
Some voidT))
|
CSCase ce cs =>
'(
e,
_) ←
to_expr Δ
l ce;
guard (
is_pure e)
with "
case with non-
constant expression";
Γ ←
gets to_env;
m ←
gets to_mem;
v ←
error_of_option (⟦
e ⟧ Γ []
m ≫=
maybe inr)
"
case with undefined expression";
'(
_,
x) ←
error_of_option (
maybe VBase v ≫=
maybe2 VInt)
"
case with non-
integer expression";
'(
s,
cmσ) ←
go bc Δ
l cs;
mret (
scase (
Some x) ;;
s,
cmσ)
|
CSDefault cs => '(
s,
cmσ) ←
go bc Δ
l cs;
mret (
scase None ;;
s,
cmσ)
|
CSScope cs =>
go bc (
None :: Δ
l)
cs
|
CSLocal stos x cτ
mce cs =>
guard (
local_fresh x Δ
l)
with
("
block scope variable `" +:+
x +:+ "`
previously declared");
match to_storage stos with
|
Some StaticStorage =>
'(
o,τ) ←
alloc_static Δ
l x cτ
mce;
go bc (
Some (
x,
Extern (
inl (
o,τ))) :: Δ
l)
cs
|
Some ExternStorage =>
guard (
mce =
None)
with ("
block scope variable `" +:+
x +:+
"`
has both `
extern`
and an initializer");
decl ←
alloc_global Δ
l x ExternStorage cτ
None;
go bc (
Some (
x,
Extern decl) :: Δ
l)
cs
|
Some AutoStorage =>
τ ←
to_type to_Type Δ
l cτ;
guard (τ ≠
voidT)
with
("
block scope variable `" +:+
x +:+ "`
of void type");
Γ ←
gets to_env;
match mce with
|
Some ce =>
e ←
to_init_expr (
Some (
x,
Local τ) :: Δ
l) τ
ce;
'(
s,
cmσ) ←
go bc (
Some (
x,
Local τ) :: Δ
l)
cs;
mret (
local{τ} (
var 0 ::=
e ;;
s),
cmσ)
|
None =>
'(
s,
cmσ) ←
go bc (
Some (
x,
Local τ) :: Δ
l)
cs;
mret (
local{τ}
s,
cmσ)
end
|
_ =>
fail ("
block scope variable `" +:+
x +:+
"`
has multiple storage specifiers")
end
|
CSTypeDef x cτ
cs =>
guard (
local_fresh x Δ
l)
with
("
typedef `" +:+
x +:+ "`
previously declared");
τ
p ←
to_type to_Ptr Δ
l cτ;
go bc (
Some (
x,
TypeDef τ
p) :: Δ
l)
cs
|
CSComp cs1 cs2 =>
'(
s1,
cmσ1) ←
go bc Δ
l cs1;
'(
s2,
cmσ2) ←
go bc Δ
l cs2;
mσ ←
error_of_option (
rettype_union_alt (
cmσ1.2) (
cmσ2.2))
"
composition of statements with non-
matching return types";
mret (
s1 ;;
s2, (
cmσ2.1,
mσ))
|
CSLabel l cs => '(
s,
cmσ) ←
go bc Δ
l cs;
mret (
l :;
s,
cmσ)
|
CSWhile ce cs =>
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry to_R;
τ
b ←
error_of_option (
maybe TBase τ)
"
conditional argument of while statement of non-
base type";
guard (τ
b ≠
TVoid)
with
"
conditional argument of while statement of void type";
'(
s,
cmσ) ←
go (
Some 1,
Some 0) Δ
l cs;
mret (
catch (
loop (
if{
e}
skip else throw 0 ;;
catch s)), (
false,
cmσ.2))
|
CSFor ce1 ce2 ce3 cs =>
'(
e1,τ1) ←
to_expr Δ
l ce1 ≫=
curry to_R;
'(
e2,τ2) ←
to_expr Δ
l ce2 ≫=
curry to_R;
'(
e3,τ3) ←
to_expr Δ
l ce3 ≫=
curry to_R;
'(
s,
cmσ) ←
go (
Some 1,
Some 0) Δ
l cs;
τ
b ←
error_of_option (
maybe TBase τ2)
"
conditional argument of for statement of non-
base type";
guard (τ
b ≠
TVoid)
with
"
conditional argument of for statement of void type";
mret (!(
cast{
voidT}
e1) ;;
catch (
loop (
if{
e2}
skip else throw 0 ;;
catch s ;; !(
cast{
voidT}
e3)
)), (
false,
cmσ.2))
|
CSDoWhile cs ce =>
'(
s,
cmσ) ←
go (
Some 1,
Some 0) Δ
l cs;
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry to_R;
τ
b ←
error_of_option (
maybe TBase τ)
"
conditional argument of do-
while statement of non-
base type";
guard (τ
b ≠
TVoid)
with
"
conditional argument of do-
while statement of void type";
mret (
catch (
loop (
catch s ;;
if{
e}
skip else throw 0)), (
false,
cmσ.2))
|
CSIf ce cs1 cs2 =>
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry to_R;
'(
s1,
cmσ1) ←
go bc Δ
l cs1;
'(
s2,
cmσ2) ←
go bc Δ
l cs2;
τ
b ←
error_of_option (
maybe TBase τ)
"
conditional argument of if statement of non-
base type";
guard (τ
b ≠
TVoid)
with
"
conditional argument of if statement of void type";
mσ ←
error_of_option (
rettype_union_alt (
cmσ1.2) (
cmσ2.2))
"
if statement with non-
matching return types";
mret (
if{
e}
s1 else s2, (
cmσ1.1 &&
cmσ2.1,
mσ))%
S
|
CSSwitch ce cs =>
'(
e,τ) ←
to_expr Δ
l ce ≫=
curry to_R;
τ
i ←
error_of_option (
maybe (
TBase ∘
TInt) τ)
"
conditional argument of switch statement of non-
integer type";
'(
s,
cmσ) ←
go (
Some 0,
S <$>
bc.2) Δ
l cs;
mret (
catch (
switch{
e}
s), (
false,
cmσ.2))
end.
Definition stmt_fix_return (Γ :
env K) (
f :
string) (σ :
type K) (
s :
stmt K)
(
cmτ :
rettype K) :
stmt K *
rettype K :=
match cmτ
with
| (
false,
_) =>
if decide (σ =
voidT)
then (
s,
cmτ)
else if decide (
f = "
main")
then (
s;;
ret (#
val_0 Γ σ), (
true,
Some σ))
else (
s;;
ret (
abort σ), (
true,
Some σ))
|
_ => (
s,
cmτ)
end.
Definition to_fun_stmt (
f :
string) (
mys :
list (
option string))
(τ
s :
list (
type K)) (σ :
type K) (
cs :
cstmt) :
M (
stmt K) :=
ys ←
error_of_option (
mapM id mys)
("
function `" +:+
f +:+ "`
has unnamed arguments");
'(
s,
cmσ) ←
to_stmt σ (
None,
None) (
zip_with (λ
y τ,
Some (
y,
Local τ))
ys τ
s)
cs;
Γ ←
gets to_env;
let (
s,
cmσ) :=
stmt_fix_return Γ
f σ
s cmσ
in
guard (
gotos s ⊆
labels s)
with
("
function `" +:+
f +:+ "`
has unbound gotos");
mret s.
Definition alloc_fun (
f :
string)
(
sto :
cstorage) (
cσ :
ctype) (
cs :
cstmt) :
M () :=
'(
cτ
s,
cτ) ←
error_of_option (
maybe2 CTFun cσ)
("
function `" +:+
f +:+ "`
whose type is not a function type");
'(
xτ
s,τ) ←
to_fun_type (
to_type to_Ptr [])
cτ
s cτ;
Γ ←
gets to_env;
guard (
Forall (
type_complete Γ) (
xτ
s.*2))
with
("
function `" +:+
f +:+ "`
with incomplete argument type");
guard (
type_complete Γ τ)
with
("
function `" +:+
f +:+ "`
with incomplete return type");
Δ
g ←
gets to_globals;
match Δ
g !!
f with
|
Some (
Fun sto' τ
s' τ'
ms) =>
guard (τ
s' =
xτ
s.*2)
with
("
argument types of function `" +:+
f
+:+ "`
do not match previously declared prototype");
guard (τ' = τ)
with
("
return type of function `" +:+
f
+:+ "`
does not match previously declared prototype");
guard (
sto =
ExternStorage ∨
sto =
sto'
∨
sto =
AutoStorage ∧
sto' =
ExternStorage)
with
("
function `" +:+
f +:+ "`
previously declared with different linkage");
guard (
ms =
None)
with ("
function `" +:+
f +:+ "`
previously completed");
s ←
to_fun_stmt f (
xτ
s.*1) (
xτ
s.*2) τ
cs;
let sto :=
if decide (
sto =
ExternStorage)
then sto'
else sto in
insert_fun f sto τ
s' τ' (
Some s)
|
Some (
Global _ _ _ _) =>
fail ("
function `" +:+
f +:+ "`
previously declared as global")
|
Some (
GlobalTypeDef _) =>
fail ("
function `" +:+
f +:+ "`
previously declared as typedef")
|
Some (
EnumVal _ _) =>
fail ("
function `" +:+
f +:+ "`
previously declared as enum tag")
|
None =>
_ ←
insert_fun f sto (
xτ
s.*2) τ
None;
s ←
to_fun_stmt f (
xτ
s.*1) (
xτ
s.*2) τ
cs;
insert_fun f sto (
xτ
s.*2) τ (
Some s)
end.
Fixpoint alloc_enum (
xces :
list (
string *
option cexpr))
(τ
i :
int_type K) (
z :
Z) :
M () :=
match xces return M ()
with
| [] =>
mret ()
| (
x,
None) ::
xces =>
Δ
g ←
gets to_globals;
guard (Δ
g !!
x =
None)
with
("
enum field `" +:+
x +:+ "`
previously declared");
guard (
int_typed z τ
i)
with
("
enum field `" +:+
x +:+ "`
has value out of range");
_ ←
insert_global_decl x (
EnumVal τ
i z);
alloc_enum xces τ
i (
z + 1)%
Z
| (
x,
Some ce) ::
xces =>
'(
e,
_) ←
to_expr []
ce;
Δ
g ←
gets to_globals;
guard (Δ
g !!
x =
None)
with
("
enum field `" +:+
x +:+ "`
previously declared");
guard (
is_pure e)
with
("
enum field `" +:+
x +:+ "`
has non-
constant value");
Γ ←
gets to_env;
m ←
gets to_mem;
v ←
error_of_option (⟦
e ⟧ Γ []
m ≫=
maybe inr)
("
enum field `" +:+
x +:+ "`
has undefined value");
'(
_,
z') ←
error_of_option (
maybe VBase v ≫=
maybe2 VInt)
("
enum field `" +:+
x +:+ "`
has non-
integer value");
guard (
int_typed z' τ
i)
with "
enum field with value out of range";
_ ←
insert_global_decl x (
EnumVal τ
i z');
alloc_enum xces τ
i (
z' + 1)%
Z
end.
Definition to_compound_fields (
t :
tag) :
list (
string *
ctype) →
M (
list (
string *
type K)) :=
fix go cτ
s :=
match cτ
s with
| [] =>
mret []
| (
x,
cτ) ::
cτ
s =>
τ ←
to_type to_Type []
cτ;
guard (τ ≠
voidT)
with
("
compound type `" +:+
t +:+ "`
with field `" +:+
x +:+ "`
of void type");
((
x,τ) ::) <$>
go cτ
s
end.
Fixpoint alloc_decls (Θ :
list (
string *
decl)) :
M () :=
match Θ
with
| [] =>
mret ()
| (
x,
CompoundDecl c cτ
s) :: Θ =>
let t :
tag :=
x in
xτ
s ←
to_compound_fields t cτ
s;
Γ ←
gets to_env;
guard (Γ !!
t =
None)
with
("
compound type `" +:+
x +:+ "`
previously declared");
guard (
NoDup (
xτ
s.*1))
with
("
compound type `" +:+
x +:+ "`
has field names that are not unique");
guard (
xτ
s ≠ [])
with
("
compound type `" +:+
x +:+ "`
declared without any fields");
_ ←
insert_compound c t xτ
s;
alloc_decls Θ
| (
x,
EnumDecl cτ
i yces) :: Θ =>
let t :
tag :=
x in
let τ
i :=
to_inttype cτ
i in
Γ
n ←
gets to_compounds;
guard (Γ
n !!
t =
None)
with
("
enum type `" +:+
x +:+ "`
previously declared");
_ ←
insert_enum t τ
i;
_ ←
alloc_enum yces τ
i 0;
alloc_decls Θ
| (
x,
TypeDefDecl cτ) :: Θ =>
τ
p ←
to_type to_Ptr []
cτ;
Δ
g ←
gets to_globals;
guard (Δ
g !!
x =
None)
with
("
typedef `" +:+
x +:+ "`
previously declared");
_ ←
insert_global_decl x (
GlobalTypeDef τ
p);
alloc_decls Θ
| (
x,
GlobDecl stos cτ
me) :: Θ =>
guard (
stos ≠ [
AutoStorage])
with
("
global `" +:+
x +:+ "`
has `
auto`
storage");
sto ←
error_of_option (
to_storage stos)
("
global `" +:+
x +:+ "`
has multiple storage specifiers");
_ ←
alloc_global []
x sto cτ
me;
alloc_decls Θ
| (
f,
FunDecl stos cσ
cs) :: Θ =>
guard (
stos ≠ [
AutoStorage])
with
("
function `" +:+
f +:+ "`
has `
auto`
storage");
sto ←
error_of_option (
to_storage stos)
("
function `" +:+
f +:+ "`
has multiple storage specifiers");
_ ←
alloc_fun f sto cσ
cs;
alloc_decls Θ
end.
Definition alloc_program (Θ :
list (
string *
decl)) :
M () :=
_ ←
alloc_decls Θ;
S ←
gets id;
let incomp_fs :
stringset :=
incomplete_fun_decls S in
guard (
incomp_fs = ∅)
with "
function `" +:+
from_option "" (
head (
elements incomp_fs)) +:+ "`
not completed";
let incomp_xs :
stringset :=
extern_global_decls S in
guard (
incomp_xs = ∅)
with "
global `" +:+
from_option "" (
head (
elements incomp_xs)) +:+ "`
with `
extern`
not linked";
mret ().
End frontend_more.