[frontend] Fix capture init for cpp lambdas

Summary:
We were missing assignment to captured variables with initializers.

Consider the following example:
```
S* update_inside_lambda_capture_and_init(S* s) {
  S* object = nullptr;
  auto f = [& o = object](S* s) { o = s; };
  f(s);
  return object;
}
```
which was translated to
```
VARIABLE_DECLARED(o:S*&);
*&o:S*&=&object
*&f =(_fun...lambda..._operator(),([by ref]&o &o:S*&))
```
However, we want to capture `o` (which is an address of `object`), rather `&o` in closure.

After the diff
```
VARIABLE_DECLARED(o:S*&);
*&o:S*&=&object
n$7=*&o:S*&
*&f =(_fun...lambda..._operator(),([by ref]n$7 &o:S*&))
```

Reviewed By: jvillard

Differential Revision: D23567346

fbshipit-source-id: 20f77acc2
master
Daiva Naudziuniene 4 years ago committed by Facebook GitHub Bot
parent d13ae13a71
commit d0cb245303

@ -3177,26 +3177,32 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
CVar_decl.sil_var_of_captured_var context stmt_info.Clang_ast_t.si_source_range procname
decl_ref
in
let translate_capture_init (pvar, typ) init_decl =
let translate_captured_var_assign pvar_typ_mode trans_results_acc captured_vars_acc =
let loc =
CLocation.location_of_stmt_info context.translation_unit_context.source_file stmt_info
in
let ((exp, _, typ, _) as exp_pvar_typ), instr = assign_captured_var loc pvar_typ_mode in
let trans_results = mk_trans_result (exp, typ) {empty_control with instrs= [instr]} in
(trans_results :: trans_results_acc, exp_pvar_typ :: captured_vars_acc)
in
let translate_capture_init mode (pvar, typ) init_decl (trans_results_acc, captured_vars_acc) =
match init_decl with
| Clang_ast_t.VarDecl (_, _, _, {vdi_init_expr}) ->
init_expr_trans trans_state (Exp.Lvar pvar, typ) stmt_info vdi_init_expr
let init_trans_results =
init_expr_trans trans_state (Exp.Lvar pvar, typ) stmt_info vdi_init_expr
:: trans_results_acc
in
translate_captured_var_assign (pvar, typ, mode) init_trans_results captured_vars_acc
| _ ->
CFrontend_errors.incorrect_assumption __POS__ stmt_info.Clang_ast_t.si_source_range
"Capture-init statement without var decl"
in
let translate_normal_capture mode (pvar, typ) (trans_results_acc, captured_vars_acc) =
let loc =
CLocation.location_of_stmt_info context.translation_unit_context.source_file stmt_info
in
match mode with
| Pvar.ByReference ->
(trans_results_acc, (Exp.Lvar pvar, pvar, typ, mode) :: captured_vars_acc)
| Pvar.ByValue ->
let pvar_typ_mode = (pvar, typ, mode) in
let ((exp, _, typ, _) as exp_pvar_typ), instr = assign_captured_var loc pvar_typ_mode in
let trans_results = mk_trans_result (exp, typ) {empty_control with instrs= [instr]} in
(trans_results :: trans_results_acc, exp_pvar_typ :: captured_vars_acc)
translate_captured_var_assign (pvar, typ, mode) trans_results_acc captured_vars_acc
in
let translate_captured
{Clang_ast_t.lci_captured_var; lci_init_captured_vardecl; lci_capture_this; lci_capture_kind}
@ -3221,9 +3227,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s
| Some captured_var_decl_ref, Some init_decl -> (
(* capture and init *)
match get_captured_pvar_typ captured_var_decl_ref with
| Some ((pvar, typ) as pvar_typ) ->
( translate_capture_init pvar_typ init_decl :: trans_results_acc
, (Exp.Lvar pvar, pvar, typ, mode) :: captured_vars_acc )
| Some pvar_typ ->
translate_capture_init mode pvar_typ init_decl acc
| None ->
(trans_results_acc, captured_vars_acc) )
| Some captured_var_decl_ref, None -> (

@ -468,17 +468,9 @@ let apply_callee callee_pname call_loc callee_exec_state ~ret ~captured_vars_wit
let get_captured_actuals location ~captured_vars ~actual_closure astate =
let* astate, this_value_addr = eval_access location actual_closure Dereference astate in
let+ _, astate, captured_vars_with_actuals =
List.fold_result captured_vars ~init:(0, astate, [])
~f:(fun (id, astate, captured) (var, mode) ->
let* astate, field =
eval_access location this_value_addr (FieldAccess (Closures.mk_fake_field ~id)) astate
in
List.fold_result captured_vars ~init:(0, astate, []) ~f:(fun (id, astate, captured) var ->
let+ astate, captured_actual =
match mode with
| Pvar.ByReference ->
eval_access location field Dereference astate
| Pvar.ByValue ->
Ok (astate, field)
eval_access location this_value_addr (FieldAccess (Closures.mk_fake_field ~id)) astate
in
(id + 1, astate, (var, captured_actual) :: captured) )
in
@ -495,9 +487,9 @@ let call ~callee_data call_loc callee_pname ~ret ~actuals ~formals_opt (astate :
in
let captured_vars =
Procdesc.get_captured callee_proc_desc
|> List.map ~f:(fun (mangled, _, mode) ->
|> List.map ~f:(fun (mangled, _, _) ->
let pvar = Pvar.mk mangled callee_pname in
(Var.of_pvar pvar, mode) )
Var.of_pvar pvar )
in
let* astate, captured_vars_with_actuals =
match actuals with

@ -69,10 +69,14 @@ void init_capture_reassign_bad() {
return [i = 1]() { return i; }();
}
void init_capture_no_call_bad() {
void FN_init_capture_no_call_bad() {
[i = 1]() { return i; };
}
void FN_init_capture_call_bad2() {
auto f = [i = 1]() { return i; };
}
int FN_init_capture_no_read_bad() {
return [i = 1]() { return 0; }();
}

@ -8,10 +8,9 @@ codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::dead_struct_no_destruct
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::dead_struct_rvalue_ref_bad, 0, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::dead_then_live_bad, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::easy_bad, 0, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::init_capture_no_call_bad, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::init_capture_reassign_bad, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::init_in_binop_bad, 0, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::lambda_bad::lambda_dead_stores.cpp:150:11::operator(), 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::lambda_bad::lambda_dead_stores.cpp:154:11::operator(), 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::plus_plus1_bad, 2, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::plus_plus2_bad, 2, DEAD_STORE, no_bucket, ERROR, [Write of unused value]
codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::plus_plus3_bad, 2, DEAD_STORE, no_bucket, ERROR, [Write of unused value]

@ -211,7 +211,7 @@ void capture_by_value_bad() {
}
}
void capture_by_ref_ok() {
void capture_by_ref_ok_FP() {
int value = 5;
auto f = [&value]() -> int* { return new int(value); };
value++;
@ -233,6 +233,50 @@ void capture_by_ref_bad() {
}
}
void capture_by_value_init_ok() {
int value = 5;
auto f = [v = value]() -> int* { return new int(v); };
value++;
int* p = f();
int* q = nullptr;
if (*p != 5) {
*q = 42;
}
}
void capture_by_value_init_bad() {
int value = 5;
auto f = [v = value]() -> int* { return new int(v); };
value++;
int* p = f();
int* q = nullptr;
if (*p == 5) {
*q = 42;
}
}
void capture_by_ref_init_ok() {
int value = 5;
auto f = [& v = value]() -> int* { return new int(v); };
value++;
int* p = f();
int* q = nullptr;
if (*p != 6) {
*q = 42;
}
}
void capture_by_ref_init_bad() {
int value = 5;
auto f = [& v = value]() -> int* { return new int(v); };
value++;
int* p = f();
int* q = nullptr;
if (*p == 6) {
*q = 42;
}
}
S* update_inside_lambda_capture_and_init(S* s) {
S* object = nullptr;
auto f = [& o = object](S* s) { o = s; };

@ -10,7 +10,10 @@ codetoanalyze/cpp/pulse/closures.cpp, call_lambda_bad, 4, USE_AFTER_DELETE, no_b
codetoanalyze/cpp/pulse/closures.cpp, call_lambda_std_fun_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,was invalidated by `delete`,use-after-lifetime part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,when calling `call_lambda_std_fun_bad::lambda_closures.cpp:171:7::operator()` here,parameter `s` of call_lambda_std_fun_bad::lambda_closures.cpp:171:7::operator(),invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, call_std_fun_constructor_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,was invalidated by `delete`,use-after-lifetime part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,when calling `call_std_fun_constructor_bad::lambda_closures.cpp:178:32::operator()` here,parameter `s` of call_std_fun_constructor_bad::lambda_closures.cpp:178:32::operator(),invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, capture_by_ref_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, capture_by_ref_init_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, capture_by_ref_ok_FP, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, capture_by_value_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, capture_by_value_init_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,variable `s` declared here,is the address of a stack variable `s` whose lifetime has ended,use-after-lifetime part of the trace starts here,variable `s` declared here,value captured by by ref as `s`,invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, reassign_lambda_capture_destroy_invoke_bad, 9, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,variable `s` declared here,is the address of a stack variable `s` whose lifetime has ended,use-after-lifetime part of the trace starts here,variable `s` declared here,value captured by by ref as `s`,invalid access occurs here]
codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,variable `s` declared here,is the address of a stack variable `s` whose lifetime has ended,use-after-lifetime part of the trace starts here,variable `s` declared here,value captured by by ref as `s`,invalid access occurs here]

@ -83,7 +83,7 @@ digraph cfg {
"init_capture1#11582985675627962568.58b9ce334267f411dc5e1c70bd53eb81_3" -> "init_capture1#11582985675627962568.58b9ce334267f411dc5e1c70bd53eb81_4" ;
"init_capture1#11582985675627962568.58b9ce334267f411dc5e1c70bd53eb81_4" [label="4: Return Stmt \n VARIABLE_DECLARED(0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10); [line 41, column 10]\n *&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10=(_fun_init_capture1::lambda_shared_lambda_lambda1.cpp:41:10::operator(),&i) [line 41, column 10]\n n$5=_fun_init_capture1::lambda_shared_lambda_lambda1.cpp:41:10::operator()(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10&) [line 41, column 10]\n _=*&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10 [line 41, column 34]\n n$2=_fun_init_capture1::lambda_shared_lambda_lambda1.cpp:41:10::~(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10*) injected [line 41, column 34]\n *&return:int=n$5 [line 41, column 3]\n " shape="box"]
"init_capture1#11582985675627962568.58b9ce334267f411dc5e1c70bd53eb81_4" [label="4: Return Stmt \n VARIABLE_DECLARED(0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10); [line 41, column 10]\n n$5=*&i:int [line 41, column 10]\n *&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10=(_fun_init_capture1::lambda_shared_lambda_lambda1.cpp:41:10::operator(),([by value]n$5 &i:int)) [line 41, column 10]\n n$6=_fun_init_capture1::lambda_shared_lambda_lambda1.cpp:41:10::operator()(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10&) [line 41, column 10]\n _=*&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10 [line 41, column 34]\n n$2=_fun_init_capture1::lambda_shared_lambda_lambda1.cpp:41:10::~(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture1::lambda_shared_lambda_lambda1.cpp:41:10*) injected [line 41, column 34]\n *&return:int=n$6 [line 41, column 3]\n " shape="box"]
"init_capture1#11582985675627962568.58b9ce334267f411dc5e1c70bd53eb81_4" -> "init_capture1#11582985675627962568.58b9ce334267f411dc5e1c70bd53eb81_2" ;
@ -102,11 +102,11 @@ digraph cfg {
"init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_4" -> "init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_3" ;
"init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_5" [label="5: DeclStmt \n VARIABLE_DECLARED(a:int); [line 46, column 10]\n n$5=*&i:int [line 46, column 15]\n *&a:int=n$5 [line 46, column 10]\n " shape="box"]
"init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_5" [label="5: DeclStmt \n VARIABLE_DECLARED(a:int); [line 46, column 10]\n n$7=*&i:int [line 46, column 15]\n *&a:int=n$7 [line 46, column 10]\n " shape="box"]
"init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_5" -> "init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_4" ;
"init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_6" [label="6: Return Stmt \n VARIABLE_DECLARED(0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10); [line 46, column 10]\n *&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10=(_fun_init_capture2::lambda_shared_lambda_lambda1.cpp:46:10::operator(),&a,&b,&c) [line 46, column 10]\n n$6=_fun_init_capture2::lambda_shared_lambda_lambda1.cpp:46:10::operator()(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10&) [line 46, column 10]\n _=*&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10 [line 46, column 56]\n n$2=_fun_init_capture2::lambda_shared_lambda_lambda1.cpp:46:10::~(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10*) injected [line 46, column 56]\n *&return:int=n$6 [line 46, column 3]\n " shape="box"]
"init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_6" [label="6: Return Stmt \n VARIABLE_DECLARED(0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10); [line 46, column 10]\n n$8=*&a:int [line 46, column 10]\n n$6=*&b:int [line 46, column 10]\n n$5=*&c:int [line 46, column 10]\n *&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10=(_fun_init_capture2::lambda_shared_lambda_lambda1.cpp:46:10::operator(),([by value]n$8 &a:int),([by value]n$6 &b:int),([by value]n$5 &c:int)) [line 46, column 10]\n n$9=_fun_init_capture2::lambda_shared_lambda_lambda1.cpp:46:10::operator()(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10&) [line 46, column 10]\n _=*&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10 [line 46, column 56]\n n$2=_fun_init_capture2::lambda_shared_lambda_lambda1.cpp:46:10::~(&0$?%__sil_tmpSIL_materialize_temp__n$0:init_capture2::lambda_shared_lambda_lambda1.cpp:46:10*) injected [line 46, column 56]\n *&return:int=n$9 [line 46, column 3]\n " shape="box"]
"init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_6" -> "init_capture2#11582143449720942167.039b5039af3b7807e4b00950523a9f3a_2" ;

Loading…
Cancel
Save