Module ErgoSpec.ErgoC.Lang.ErgoCType
Require Import String.
Require Import List.
Require Import Basics.
Require Import ErgoSpec.Backend.ErgoBackend.
Require Import ErgoSpec.Common.Utils.Misc.
Require Import ErgoSpec.Common.Utils.Names.
Require Import ErgoSpec.Common.Utils.NamespaceContext.
Require Import ErgoSpec.Common.Utils.Result.
Require Import ErgoSpec.Common.Utils.Provenance.
Require Import ErgoSpec.Common.Utils.Ast.
Require Import ErgoSpec.Common.Utils.PrintTypedData.
Require Import ErgoSpec.Common.Types.ErgoTypetoErgoCType.
Require Import ErgoSpec.ErgoC.Lang.ErgoC.
Require Import ErgoSpec.ErgoC.Lang.ErgoCTypeContext.
Require Import ErgoSpec.Ergo.Lang.Ergo.
Section ErgoCType.
Context {
m :
brand_model}.
Import ErgoCTypes.
Definition ergoc_type_join_safe prov (
t1 t2:
ergoc_type) :
eresult ergoc_type :=
let jt :=
ergoc_type_join t1 t2 in
if ergoc_type_subtype_dec ttop jt
then efailure (
ETypeError prov ("
Join between types is TOP.")%
string)
else esuccess jt.
Definition ergoc_type_meet_safe prov (
t1 t2:
ergoc_type) :
eresult ergoc_type :=
let jt :=
ergoc_type_meet t1 t2 in
if ergoc_type_subtype_dec jt tbottom
then efailure (
ETypeError prov ("
Meet between types is BOTTOM.")%
string)
else esuccess jt.
Program Definition empty_rec_type :
ergoc_type :=
Rec Closed nil _.
Definition ergo_format_unop_error nsctxt (
op :
unary_op) (
arg :
ergoc_type) :
string :=
let fmt_easy :=
fun name expected actual =>
("
Operator `" ++
name ++ "'
expected an operand of type `" ++
(
ergoc_type_to_string nsctxt expected) ++
"'
but received an operand of type `" ++
(
ergoc_type_to_string nsctxt actual) ++ "'.")%
string
in
match op with
|
OpNeg =>
fmt_easy "!"%
string tbool arg
|
OpFloatUnary FloatNeg =>
fmt_easy "-"%
string tfloat arg
|
OpDot name => "
The field " ++
name ++ "
does not belong to type `" ++ (
ergoc_type_to_string nsctxt arg) ++ "'"
|
_ => "
This operator received an unexpected argument of type `" ++ (
ergoc_type_to_string nsctxt arg) ++ "'"
end.
Definition ergo_format_binop_error nsctxt (
op :
binary_op) (
arg1 :
ergoc_type) (
arg2 :
ergoc_type) :
string :=
let fmt_easy :=
fun name e1 e2 =>
("
Operator `" ++
name ++ "'
expected operands of type `" ++
(
ergoc_type_to_string nsctxt e1) ++ "'
and `" ++
(
ergoc_type_to_string nsctxt e2) ++
"'
but received operands of type `" ++
(
ergoc_type_to_string nsctxt arg1) ++ "'
and `" ++
(
ergoc_type_to_string nsctxt arg2) ++ "'.")%
string
in
match op with
|
OpAnd =>
fmt_easy "
and"%
string tbool tbool
|
OpOr =>
fmt_easy "
or"%
string tbool tbool
|
OpFloatBinary FloatPlus =>
fmt_easy "+"%
string tfloat tfloat
|
OpFloatBinary FloatMinus =>
fmt_easy "-"%
string tfloat tfloat
|
OpFloatBinary FloatMult =>
fmt_easy "*"%
string tfloat tfloat
|
OpFloatBinary FloatDiv =>
fmt_easy "/"%
string tfloat tfloat
|
OpFloatBinary FloatPow =>
fmt_easy "^"%
string tfloat tfloat
|
OpNatBinary NatPlus =>
fmt_easy "+
i"%
string tnat tnat
|
OpNatBinary NatMinus =>
fmt_easy "-
i"%
string tnat tnat
|
OpNatBinary NatMult =>
fmt_easy "*
i"%
string tnat tnat
|
OpNatBinary NatDiv =>
fmt_easy "/
i"%
string tnat tnat
|
OpNatBinary NatPow =>
fmt_easy "^
i"%
string tnat tnat
|
OpFloatCompare FloatLt =>
fmt_easy "<"%
string tfloat tfloat
|
OpFloatCompare FloatLe =>
fmt_easy "<="%
string tfloat tfloat
|
OpFloatCompare FloatGt =>
fmt_easy ">"%
string tfloat tfloat
|
OpFloatCompare FloatGe =>
fmt_easy ">="%
string tfloat tfloat
|
_ => "
This operator received unexpected arguments of type `" ++ (
ergoc_type_to_string nsctxt arg1) ++ "' " ++ "
and `" ++ (
ergoc_type_to_string nsctxt arg2) ++ "'."
end.
Definition ergo_format_new_error nsctxt (
name:
string) (
actual:
ergoc_type) :
string :=
let concept_name :=
ergoc_type_to_string nsctxt (
Brand (
name::
nil))
in
match diff_record_types (
name::
nil)
actual with
|
None => "
Concept name " ++
name ++ "
does not match data"
|
Some (
nil,
nil) =>
match fields_that_are_not_subtype (
name::
nil)
actual with
|
nil => "
Concept " ++
name ++ "
doesn'
t match data (
one field is not a subtype)"
| (
expected_name,
expected_type,
actual_type) ::
_ =>
"
Field `" ++
expected_name
++ "'
has type `" ++ (
ergoc_type_to_string nsctxt actual_type)
++ "'
but should have type `" ++ (
ergoc_type_to_string nsctxt expected_type) ++ "'"
end
|
Some (
nil,
actual_name::
nil) =>
"
Unknown field `" ++
actual_name ++ "'
in type `" ++
concept_name ++ "'"
|
Some (
nil,
actual_names) =>
"
Unknown fields `" ++
String.concat "', `"
actual_names ++ "'
in type `" ++
concept_name ++ "'"
|
Some (
expected_name::
nil,
_) =>
"
Missing field `" ++
expected_name ++ "'
in type `" ++
concept_name ++ "'"
|
Some (
expected_names,
_) =>
"
Missing fields `" ++
String.concat "', `"
expected_names ++ "'
in type `" ++
concept_name ++ "'"
end.
Definition ergo_format_clause_return_fallback_error
nsctxt
(
name:
string)
(
actual expected:
ergoc_type) :
string :=
let actual_s :=
ergoc_type_to_string nsctxt actual in
let expected_s :=
ergoc_type_to_string nsctxt expected in
"
Clause " ++
name ++ "
should return `" ++
expected_s
++ "'
but actually returns `" ++
actual_s ++ "'".
Definition ergo_format_clause_return_component_error
nsctxt
(
name:
string)
(
component1 component2:
string)
(
actual expected:
ergoc_type) :
string :=
let actual_s :=
ergoc_type_to_string nsctxt actual in
let expected_s :=
ergoc_type_to_string nsctxt expected in
"
Clause " ++
name ++ "
should " ++
component1 ++ " `" ++
expected_s
++ "'
but actually " ++
component2 ++ " `" ++
actual_s ++ "'".
Definition ergo_format_clause_return_normal_error
nsctxt
(
name:
string)
(
actual expected:
ergoc_type)
(
actual_quad expected_quad:
ergoc_type *
ergoc_type *
ergoc_type *
ergoc_type)
:
string :=
let '(
actual_resp,
actual_emit,
actual_state,
actual_error) :=
actual_quad in
let '(
expected_resp,
expected_emit,
expected_state,
expected_error) :=
expected_quad in
if ergoc_type_subtype_dec actual_resp expected_resp
then
if ergoc_type_subtype_dec actual_emit expected_emit
then
if ergoc_type_subtype_dec actual_state expected_state
then
if ergoc_type_subtype_dec actual_error expected_error
then
ergo_format_clause_return_fallback_error nsctxt name actual expected
else
ergo_format_clause_return_component_error
nsctxt name "
fail with" "
fails with"
actual_error expected_error
else
ergo_format_clause_return_component_error
nsctxt name "
set state" "
sets state"
actual_state expected_state
else
ergo_format_clause_return_component_error
nsctxt name "
emit" "
emits"
actual_emit expected_emit
else
ergo_format_clause_return_component_error
nsctxt name "
respond" "
responds"
actual_resp expected_resp.
Definition ergo_format_clause_return_error nsctxt (
name:
string) (
actual expected:
ergoc_type) :
string :=
let actual_quad :=
unpack_output_type nsctxt actual in
let expected_quad :=
unpack_output_type nsctxt expected in
let normal_error :=
ergo_format_clause_return_normal_error nsctxt name actual expected in
let fallback_error :=
fun e =>
ergo_format_clause_return_fallback_error nsctxt name actual expected in
elift2_both
normal_error
fallback_error
actual_quad
expected_quad.
Definition ergo_format_function_return_error nsctxt (
name:
string) (
actual expected:
ergoc_type) :
string :=
let actual_s :=
ergoc_type_to_string nsctxt actual in
let expected_s :=
ergoc_type_to_string nsctxt expected in
"
Function " ++
name ++ "
should return `" ++
expected_s ++ "'
but actually returns `" ++
actual_s ++ "'".
Fixpoint ergo_type_expr nsctxt (
ctxt :
type_context) (
expr :
ergoc_expr) :
eresult ergoc_type :=
match expr with
|
EThisContract prov =>
efailure (
ESystemError prov "
No `
this'
in ergoc")
|
EThisClause prov =>
efailure (
ESystemError prov "
No `
clause'
in ergoc")
|
EThisState prov =>
efailure (
ESystemError prov "
No `
state'
in ergoc")
|
EVar prov name =>
let opt :=
lookup String.string_dec (
ctxt.(
type_context_local_env)++
ctxt.(
type_context_global_env))
name in
eresult_of_option opt (
ETypeError prov ("
Variable `" ++
name ++ "'
not found.")%
string)
|
EConst prov d =>
eresult_of_option
(
infer_data_type d)
(
ETypeError prov "
Bad constant.")
|
ENone prov =>
esuccess (
toption tbottom)
|
ESome prov e =>
elift toption (
ergo_type_expr nsctxt ctxt e)
|
EArray prov es =>
(
elift tcoll)
(
fold_left
(
fun T new =>
eolift
(
fun T' =>
elift
(
fun new' =>
ergoc_type_join T'
new')
(
ergo_type_expr nsctxt ctxt new))
T)
es (
esuccess tbottom))
|
EUnaryOp prov op e =>
match ergo_type_expr nsctxt ctxt e with
|
Success _ _ t =>
match ergoc_type_infer_unary_op op t with
|
Some (
r,
_) =>
esuccess r
|
None =>
efailure (
ETypeError prov (
ergo_format_unop_error nsctxt op t))
end
|
Failure _ _ f =>
efailure f
end
|
EBinaryOp prov op e1 e2 =>
match ergo_type_expr nsctxt ctxt e1 with
|
Success _ _ t1 =>
match ergo_type_expr nsctxt ctxt e2 with
|
Success _ _ t2 =>
match ergoc_type_infer_binary_op op t1 t2 with
|
Some (
r,
_,
_) =>
esuccess r
|
None =>
efailure (
ETypeError prov (
ergo_format_binop_error nsctxt op t1 t2))
end
|
Failure _ _ f =>
efailure f
end
|
Failure _ _ f =>
efailure f
end
|
EIf prov c t f =>
eolift (
fun c' =>
if ergoc_type_subtype_dec c'
tbool then
elift2 ergoc_type_join
(
ergo_type_expr nsctxt ctxt t)
(
ergo_type_expr nsctxt ctxt f)
else efailure (
ETypeError prov "'
If'
condition not boolean."%
string))
(
ergo_type_expr nsctxt ctxt c)
|
ELet prov n None v e =>
(
eolift (
fun vt =>
let ctxt' :=
type_context_update_local_env ctxt n vt in
ergo_type_expr nsctxt ctxt'
e)
(
ergo_type_expr nsctxt ctxt v))
|
ELet prov n (
Some t)
v e =>
let fmt_err :=
fun t'
vt =>
match prov with
|
ProvFunc _ fname =>
ETypeError prov
("
Function `" ++
fname
++ "'
expected argument `"
++
n
++ "'
to be of type `"
++ (
ergoc_type_to_string nsctxt t')
++ "'
but was given argument of type `"
++ (
ergoc_type_to_string nsctxt vt)
++ "'." )
|
_ =>
ETypeError prov
("
The let type annotation `"
++ (
ergoc_type_to_string nsctxt t')
++ "'
for the name `"
++
n
++ "'
does not match the actual type `"
++ (
ergoc_type_to_string nsctxt vt)
++ "'.")
end
in
(
eolift
(
fun vt =>
let t' := (
ergo_type_to_ergoc_type t)
in
if subtype_dec vt t'
then
let ctxt' :=
type_context_update_local_env
ctxt n
t'
in
ergo_type_expr nsctxt ctxt'
e
else
efailure (
fmt_err t'
vt))
(
ergo_type_expr nsctxt ctxt v))
|
ERecord prov rs =>
fold_left
(
fun sofar next =>
eolift2
(
fun sofar'
val' =>
(
elift (
compose fst fst))
(
eresult_of_option
(
ergoc_type_infer_binary_op OpRecConcat sofar'
val')
(
ETypeError prov "
Bad record!
Failed to concat."%
string)))
sofar
(
eolift (
fun val =>
(
elift fst)
(
eresult_of_option
(
ergoc_type_infer_unary_op (
OpRec (
fst next))
val)
(
ETypeError prov "
Bad record!
Failed to init."%
string)))
(
ergo_type_expr nsctxt ctxt (
snd next))))
rs (
esuccess empty_rec_type)
|
ENew prov name rs =>
eolift
(
fun rs' =>
(
elift fst)
(
eresult_of_option
(
infer_brand_strict (
name::
nil)
rs')
(
ETypeError prov (
ergo_format_new_error nsctxt name rs'))))
(
fold_left
(
fun sofar next =>
eolift2
(
fun sofar'
val' =>
(
elift (
compose fst fst))
(
eresult_of_option
(
ergoc_type_infer_binary_op OpRecConcat sofar'
val')
(
ETypeError prov "
Bad record!
Failed to concat."%
string)))
sofar
(
eolift (
fun val =>
(
elift fst)
(
eresult_of_option
(
ergoc_type_infer_unary_op (
OpRec (
fst next))
val)
(
ETypeError prov "
Bad record!
Failed to init."%
string)))
(
ergo_type_expr nsctxt ctxt (
snd next))))
rs (
esuccess empty_rec_type))
|
ECallFun prov fname args =>
function_not_inlined_error prov fname
|
ECallFunInGroup prov gname fname args =>
function_in_group_not_inlined_error prov gname fname
|
EMatch prov term pes default =>
match ergo_type_expr nsctxt ctxt term with
|
Failure _ _ f =>
efailure f
|
Success _ _ t0 =>
fold_left
(
fun default_result pe =>
match pe with
| (
CaseData prov d,
res) =>
match ergoc_type_infer_data d with
|
None =>
efailure (
ETypeError prov "
Ill-
typed data literal!")
|
Some dt =>
elift2 ergoc_type_join
default_result
(
ergo_type_expr nsctxt ctxt res)
end
| (
CaseWildcard prov None,
res) =>
elift2 ergoc_type_join default_result (
ergo_type_expr nsctxt ctxt res)
| (
CaseLet prov name None,
res) =>
elift2 ergoc_type_join default_result
(
ergo_type_expr nsctxt (
type_context_update_local_env ctxt name t0)
res)
| (
CaseLetOption prov name None,
res) =>
match unteither t0 with
|
None =>
default_result
|
Some (
st,
ft) =>
elift2 ergoc_type_join default_result
(
ergo_type_expr nsctxt (
type_context_update_local_env ctxt name st)
res)
end
| (
CaseWildcard prov (
Some b),
res) =>
elift2 ergoc_type_join default_result
(
ergo_type_expr nsctxt ctxt res)
| (
CaseLet prov name (
Some b),
res) =>
elift2 ergoc_type_join default_result
(
ergo_type_expr nsctxt (
type_context_update_local_env
ctxt
name
(
tbrand (
b::
nil)))
res)
| (
CaseLetOption prov name (
Some b),
res) =>
elift2 ergoc_type_join default_result
(
ergo_type_expr nsctxt (
type_context_update_local_env
ctxt
name
(
tbrand (
b::
nil)))
res)
end)
pes (
ergo_type_expr nsctxt ctxt default)
end
|
EForeach prov ((
name,
arr)::
nil)
None fn =>
eolift (
fun arr' =>
eolift
(
fun typ => (
elift tcoll) (
ergo_type_expr nsctxt (
type_context_update_local_env ctxt name typ)
fn))
(
eresult_of_option
(
untcoll arr')
(
ETypeError
prov
("
foreach expects an array to iterate over,
but was given something of type `" ++ (
ergoc_type_to_string nsctxt arr') ++ "'."))))
(
ergo_type_expr nsctxt ctxt arr)
|
EForeach prov _ _ _ =>
complex_foreach_in_calculus_error prov
end.
Definition ergoc_type_function
(
nsctxt:
namespace_ctxt)
(
name:
string)
(
dctxt :
type_context)
(
func :
ergoc_function) :
eresult type_context :=
match func.(
functionc_body)
with
|
None =>
esuccess dctxt
|
Some body =>
let tsig :=
map (
fun x => (
fst x,
ergo_type_to_ergoc_type (
snd x)))
func.(
functionc_sig).(
sigc_params)
in
eolift
(
fun outt =>
let eoutt :=
func.(
functionc_sig).(
sigc_output)
in
match eoutt with
|
None =>
esuccess dctxt
|
Some eoutt' =>
let expectedt :=
ergo_type_to_ergoc_type eoutt'
in
if subtype_dec
outt
expectedt
then esuccess dctxt
else
let body_prov :=
bodyc_annot func in
match func.(
functionc_annot)
with
|
ProvClause _ name =>
efailure (
ETypeError body_prov (
ergo_format_clause_return_error nsctxt name outt expectedt))
|
ProvFunc _ name =>
efailure (
ETypeError body_prov (
ergo_format_function_return_error nsctxt name outt expectedt))
|
_ =>
efailure (
ETypeError body_prov (
ergo_format_function_return_error nsctxt name outt expectedt))
end
end)
(
ergo_type_expr nsctxt (
type_context_set_local_env dctxt tsig)
body)
end.
Definition ergoc_type_clause
(
nsctxt:
namespace_ctxt)
(
dctxt :
type_context)
(
cl :
string *
ergoc_function) :
eresult type_context :=
let (
name,
body) :=
cl in
ergoc_type_function nsctxt name dctxt body.
Definition ergoc_type_contract
(
nsctxt:
namespace_ctxt)
(
dctxt :
type_context)
(
coname:
absolute_name)
(
c :
ergoc_contract) :
eresult type_context :=
elift_fold_left
(
ergoc_type_clause nsctxt)
c.(
contractc_clauses)
dctxt.
Definition ergoc_type_decl
(
nsctxt:
namespace_ctxt)
(
dctxt :
type_context)
(
decl :
ergoc_declaration)
:
eresult (
option ergoc_type *
type_context) :=
match decl with
|
DCExpr prov expr =>
elift (
fun x => (
Some x,
dctxt)) (
ergo_type_expr nsctxt dctxt expr)
|
DCConstant prov name None expr =>
let expr' :=
ergo_type_expr nsctxt dctxt expr in
eolift (
fun val =>
esuccess (
None,
type_context_update_global_env dctxt name val))
expr'
|
DCConstant prov name (
Some t)
expr =>
let fmt_err :=
fun t'
vt =>
ETypeError prov
("
The type annotation `"
++ (
ergoc_type_to_string nsctxt t')
++ "'
for the constant `"
++
name
++ "'
does not match its actual type `"
++ (
ergoc_type_to_string nsctxt vt)
++ "'.")
in
let expr' :=
ergo_type_expr nsctxt dctxt expr in
eolift (
fun vt =>
let t' := (
ergo_type_to_ergoc_type t)
in
if subtype_dec vt t'
then
let ctxt' :=
type_context_update_global_env dctxt name t'
in
esuccess (
None,
ctxt')
else
efailure (
fmt_err t'
vt))
expr'
|
DCFunc prov name func =>
elift (
fun ctxt => (
None,
ctxt)) (
ergoc_type_function nsctxt name dctxt func)
|
DCContract prov name contr =>
elift (
fun ctxt => (
None,
ctxt)) (
ergoc_type_contract nsctxt dctxt name contr)
end.
Definition ergoc_type_module
(
nsctxt:
namespace_ctxt)
(
dctxt :
type_context)
(
mod :
ergoc_module)
:
eresult (
ergoc_module *
type_context) :=
elift (
fun x => (
mod,
snd x))
(
elift_context_fold_left
(
ergoc_type_decl nsctxt)
mod.(
modulec_declarations)
dctxt).
End ErgoCType.