[erl-frontend] Translation of some expressions

Summary:
Generates Sil instructions for Erlang some expressions:
  - empty list: e.g. []
  - cons: e.g. [1,2]
  - atom literals: e.g. ok
  - string literals: e.g. "foo"
  - calls to particular (no higher-order), unqualified functions

Differential Revision: D28996467

fbshipit-source-id: fc7f7e3e9
master
Radu Grigore 3 years ago committed by Facebook GitHub Bot
parent 66746e2b4b
commit 158bfa7a7a

@ -76,6 +76,10 @@ let __delete_array = create_procname "__delete_array"
let __delete_locked_attribute = create_procname "__delete_locked_attribute"
let __erlang_make_cons = create_procname "__erlang_make_cons"
let __erlang_make_nil = create_procname "__erlang_make_nil"
let __erlang_pattern_fail = create_procname "__erlang_pattern_fail"
let __exit = create_procname "_exit"

@ -13,7 +13,11 @@ include BUILTINS.S with type t = Procname.t
val is_declared : Procname.t -> bool
val __erlang_pattern_fail : t
val __erlang_make_cons : Procname.t
val __erlang_make_nil : Procname.t
val __erlang_pattern_fail : Procname.t
(** all patterns in a function failed to match *)
val __infer_initializer_list : Procname.t

@ -80,6 +80,7 @@ module Node = struct
| Destruction of destruction_kind
| Erlang
| ErlangCaseClause
| ErlangExpression
| ExceptionHandler
| ExceptionsSink
| ExprWithCleanups
@ -334,6 +335,8 @@ module Node = struct
F.pp_print_string fmt "Erlang (generic)"
| ErlangCaseClause ->
F.pp_print_string fmt "ErlangCaseClause"
| ErlangExpression ->
F.pp_print_string fmt "ErlangExpression"
| ExceptionHandler ->
F.pp_print_string fmt "exception handler"
| ExceptionsSink ->

@ -61,6 +61,7 @@ module Node : sig
| Destruction of destruction_kind
| Erlang
| ErlangCaseClause
| ErlangExpression
| ExceptionHandler
| ExceptionsSink
| ExprWithCleanups

@ -13,15 +13,17 @@ open! IStd
(** {2 Basics} *)
type module_reference = ModuleName of string | ModuleMissing | ModuleVariable of string
[@@deriving sexp_of]
type function_reference = FunctionName of string | FunctionVariable of string
type function_reference = FunctionName of string | FunctionVariable of string [@@deriving sexp_of]
(* NOTE: Arity could be an expression but we don't handle that case, yet. *)
type function_ = {module_: module_reference; function_: function_reference; arity: int}
[@@deriving sexp_of]
type line = int
type line = int [@@deriving sexp_of]
type record_name = string
type record_name = string [@@deriving sexp_of]
type binary_operator =
| Add
@ -51,22 +53,24 @@ type binary_operator =
| Send
| Sub
| Xor
[@@deriving sexp_of]
type unary_operator = UBNot | UMinus | UNot
type unary_operator = UBNot | UMinus | UNot [@@deriving sexp_of]
type association_kind = Arrow | Exact
type association_kind = Arrow | Exact [@@deriving sexp_of]
type exception_ = Atom of string | Pattern of string
type exception_ = Atom of string | Pattern of string [@@deriving sexp_of]
type type_specifier = (* TODO *) unit
type type_specifier = (* TODO *) unit [@@deriving sexp_of]
(** {2 S8.2: Atomic literals} *)
type literal = Atom of string | Char of string | Float of float | Int of string | String of string
[@@deriving sexp_of]
(** {2 S8.4: Expressions} *)
type body = expression list
type body = expression list [@@deriving sexp_of]
and simple_expression =
| BinaryOperator of expression * binary_operator * expression
@ -93,37 +97,42 @@ and simple_expression =
| Tuple of expression list
| UnaryOperator of unary_operator * expression
| Variable of string
[@@deriving sexp_of]
and expression = {line: line; simple_expression: simple_expression}
and expression = {line: line; simple_expression: simple_expression} [@@deriving sexp_of]
and qualifier =
| BitsGenerator of {pattern: pattern; expression: expression}
| Filter of expression
| Generator of {pattern: pattern; expression: expression}
[@@deriving sexp_of]
and timeout = {time: expression; handler: body}
and timeout = {time: expression; handler: body} [@@deriving sexp_of]
and bin_element =
{expression: expression; size: expression option; types: type_specifier list option}
[@@deriving sexp_of]
(* A [None] field stands for _, which means "all other fields". *)
and record_update = {field: string option; expression: expression}
and record_update = {field: string option; expression: expression} [@@deriving sexp_of]
and association = {kind: association_kind; key: expression; value: expression}
and association = {kind: association_kind; key: expression; value: expression} [@@deriving sexp_of]
and pattern = expression
and pattern = expression [@@deriving sexp_of]
and guard_test = expression
and guard_test = expression [@@deriving sexp_of]
(** {2 S8.5 Clauses} *)
and 'pat clause = {line: line; patterns: 'pat list; guards: guard_test list list; body: body}
[@@deriving sexp_of]
and case_clause = pattern clause
and case_clause = pattern clause [@@deriving sexp_of]
and catch_clause = catch_pattern clause
and catch_clause = catch_pattern clause [@@deriving sexp_of]
and catch_pattern = {exception_: exception_; pattern: pattern; variable: string}
[@@deriving sexp_of]
(** {2 S8.1: Module declarations and forms} *)
@ -134,7 +143,8 @@ type simple_form =
| Module of string
| File of {path: string}
| Function of {function_: function_; clauses: case_clause list}
[@@deriving sexp_of]
type form = {line: line; simple_form: simple_form}
type form = {line: line; simple_form: simple_form} [@@deriving sexp_of]
type module_ = form list
type module_ = form list [@@deriving sexp_of]

@ -246,14 +246,113 @@ let rec translate_pattern env (value : Ident.t) {Ast.line; simple_expression} =
let exit_success = Node.make_stmt env [store] in
let exit_failure = Node.make_nop env in
{start= exit_success; exit_success; exit_failure}
| _ ->
| e ->
(* TODO: Cover all cases. *)
L.debug Capture Verbose "@[todo ErlangTranslate.translate_pattern %s@."
(Sexp.to_string (Ast.sexp_of_simple_expression e)) ;
make_block_failure env
let translate_body env _body =
(* TODO: This is a dummy implementation. *)
make_block_success env
let rec instructions_of_expression env {Ast.line; simple_expression} : Sil.instr list =
let env = update_location line env in
let any = typ_of_name Any in
let procdesc = Option.value_exn env.procdesc in
let procname = Procdesc.get_proc_name procdesc in
let ret_var =
match env.result with Some (Var ret_var) -> ret_var | _ -> Ident.create_fresh Ident.knormal
in
let instructions =
match simple_expression with
| Call
{ module_= None
; function_= {Ast.line= _; simple_expression= Literal (Atom function_name)}
; args } ->
let arity = List.length args in
let callee_procname =
let module_name =
let uf_name = {UnqualifiedFunction.name= function_name; arity} in
match UnqualifiedFunction.Map.find env.imports uf_name with
| Some module_name ->
module_name
| None ->
env.current_module
in
Procname.make_erlang ~module_name ~function_name ~arity
in
let args_with_ids = List.map ~f:(fun a -> (a, Ident.create_fresh Ident.knormal)) args in
let args_instructions =
let f (one_arg_expression, one_arg_ret_var) =
let result = Some (Exp.Var one_arg_ret_var) in
instructions_of_expression {env with result} one_arg_expression
in
List.concat_map ~f args_with_ids
in
let fun_exp = Exp.Const (Cfun callee_procname) in
let args_ids_and_types =
List.map ~f:(function _, id -> (Exp.Var id, any)) args_with_ids
in
args_instructions
@ [Sil.Call ((ret_var, any), fun_exp, args_ids_and_types, env.location, CallFlags.default)]
| Cons {head; tail} ->
let head_var = Ident.create_fresh Ident.knormal in
let tail_var = Ident.create_fresh Ident.knormal in
let head_instructions =
instructions_of_expression {env with result= Some (Var head_var)} head
in
let tail_instructions =
instructions_of_expression {env with result= Some (Var tail_var)} tail
in
let fun_exp = Exp.Const (Cfun BuiltinDecl.__erlang_make_cons) in
let args : (Exp.t * Typ.t) list = [(Var head_var, any); (Var tail_var, any)] in
head_instructions @ tail_instructions
@ [Sil.Call ((ret_var, any), fun_exp, args, env.location, CallFlags.default)]
| Literal (Atom atom) ->
let hash =
(* With this hack, an atom may accidentaly be considered equal to an unrelated integer.
The [lsl] below makes this less likely. Proper fix is TODO (T93513105). *)
String.hash atom lsl 16
in
let e = Exp.Const (Cint (IntLit.of_int hash)) in
[Sil.Load {id= ret_var; e; root_typ= any; typ= any; loc= env.location}]
| Literal (String s) ->
let e = Exp.Const (Cstr s) in
[Sil.Load {id= ret_var; e; root_typ= any; typ= any; loc= env.location}]
| Nil ->
let fun_exp = Exp.Const (Cfun BuiltinDecl.__erlang_make_nil) in
[Sil.Call ((ret_var, any), fun_exp, [], env.location, CallFlags.default)]
| Variable vname ->
let e = Exp.Lvar (Pvar.mk (Mangled.from_string vname) procname) in
[Sil.Load {id= ret_var; e; root_typ= any; typ= any; loc= env.location}]
| todo ->
L.debug Capture Verbose "@[todo ErlangTranslate.instructions_of_expression %s@."
(Sexp.to_string (Ast.sexp_of_simple_expression todo)) ;
[]
in
match env.result with
| None | Some (Var _) ->
instructions
| Some result ->
instructions
@ [Sil.Store {e1= result; root_typ= any; typ= any; e2= Var ret_var; loc= env.location}]
let translate_expression env expression =
let instructions = instructions_of_expression env expression in
let exit_success = Node.make_stmt env ~kind:ErlangExpression instructions in
let exit_failure = Node.make_nop env in
{start= exit_success; exit_success; exit_failure}
let translate_body env body : block =
let blocks =
let f rev_blocks one_expression =
let env = {env with result= None} in
translate_expression env one_expression :: rev_blocks
in
let f_last rev_blocks one_expression = translate_expression env one_expression :: rev_blocks in
List.rev (IList.fold_last body ~init:[] ~f ~f_last)
in
all_blocks env blocks
(** Assumes that the values on which patterns should be matched have been loaded into the

Loading…
Cancel
Save