From b86af1e5d1890462ae5b391043a283df21404bbf Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Mon, 19 Oct 2015 03:27:32 -0700 Subject: [PATCH] Fix type information about reference types in CFGs Summary: public Two cases were not handled properly so far: 1. Declaration of a reference variable missed reference bit in type 2. Parameters to a function expecting T& had type T. The way to distinguish reference types from value types is to look whether parameter is type 'T rvalue' or type 'T lvalue' (xvalue probably as well) Unfortunately, we can't just say 'T lvalue' = 'T&' because it would break a lot of things in our frontend. However, we know that when parameter to a function call has type 'T lvalue', it has to be 'T&' type. Same applies when init_expression type is lvalue. So, the solution is to add wrapper function that looks at results of `instruction` function and expected expression type. Then if it's lvalue, wrap the type in reference. Do this wrapping magic only when we know that lvalue mean reference type. The rest of the changes is to make frontend tests pass - since we use different fields in the AST, some of them were incorrectly set before and no one noticed. Reviewed By: cristianoc Differential Revision: D2549991 fb-gh-sync-id: 067f5d5 --- infer/src/clang/ast_expressions.ml | 9 ++++--- infer/src/clang/cTrans.ml | 25 ++++++++++++++++--- .../cpp/frontend/reference/box.cpp.dot | 2 +- .../cpp/frontend/reference/init.cpp.dot | 6 ++--- .../reference/reference_struct_e2e.cpp.dot | 8 +++--- .../reference/reference_type_e2e.cpp.dot | 8 +++--- .../cpp/frontend/reference/unbox.cpp.dot | 6 ++--- 7 files changed, 41 insertions(+), 23 deletions(-) diff --git a/infer/src/clang/ast_expressions.ml b/infer/src/clang/ast_expressions.ml index 82717a1bb..a3706194f 100644 --- a/infer/src/clang/ast_expressions.ml +++ b/infer/src/clang/ast_expressions.ml @@ -389,7 +389,7 @@ let translate_dispatch_function block_name stmt_info stmt_list ei n = di_source_range = stmt_info.si_source_range } in let stmt_info = { stmt_info with si_pointer = Ast_utils.get_fresh_pointer(); } in let var_decl_info = { empty_var_decl_info with vdi_init_expr = Some block_def} in - let block_var_decl = VarDecl(decl_info, block_name_info, ei.ei_type_ptr, var_decl_info) in + let block_var_decl = VarDecl(decl_info, block_name_info, tp, var_decl_info) in let decl_stmt = DeclStmt(stmt_info,[], [block_var_decl]) in let expr_info_call = make_general_expr_info create_void_star_type `XValue `Ordinary in @@ -515,7 +515,7 @@ let translate_block_enumerate block_name stmt_info stmt_list ei = let type_opt = Some create_BOOL_type in let parameter = Clang_ast_t.UnaryExprOrTypeTraitExpr ((fresh_stmt_info stmt_info), [], - make_expr_info create_unsigned_long_type, + make_general_expr_info create_unsigned_long_type `RValue `Ordinary, { Clang_ast_t.uttei_kind = `SizeOf; Clang_ast_t.uttei_type_ptr = type_opt}) in let pointer = di.Clang_ast_t.di_pointer in let stmt_info = fresh_stmt_info stmt_info in @@ -576,7 +576,7 @@ let translate_block_enumerate block_name stmt_info stmt_list ei = let open Clang_ast_t in match pobj with | ParmVarDecl(di_obj, name_obj, tp_obj, _) -> - let poe_ei = make_general_expr_info tp_obj `LValue `Ordinary in + let poe_ei = make_general_expr_info tp_obj `RValue `Ordinary in let ei_array = get_ei_from_cast decl_ref_expr_array in let ove_array = build_OpaqueValueExpr (fresh_stmt_info stmt_info) decl_ref_expr_array ei_array in let ei_idx = get_ei_from_cast decl_ref_expr_idx in @@ -697,7 +697,8 @@ let create_assume_not_null_call decl_info var_name var_type = let cast_info_call = { Clang_ast_t.cei_cast_kind = `LValueToRValue; cei_base_path = [] } in let decl_ref_exp_cast = Clang_ast_t.ImplicitCastExpr (stmt_info, [var_decl_ref], expr_info, cast_info_call) in let null_expr = create_integer_literal stmt_info "0" in - let bin_op = make_binary_stmt decl_ref_exp_cast null_expr stmt_info (make_lvalue_obc_prop_expr_info var_type) boi in + let bin_op_expr_info = make_general_expr_info create_BOOL_type `RValue `Ordinary in + let bin_op = make_binary_stmt decl_ref_exp_cast null_expr stmt_info bin_op_expr_info boi in let parameters = [bin_op] in let procname = Procname.to_string SymExec.ModelBuiltins.__infer_assume in let qual_procname = Ast_utils.make_name_decl procname in diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index ae8a0f732..b4965a48f 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -187,6 +187,17 @@ struct { empty_res_trans with exps = [(Sil.Sizeof(expanded_type, Sil.Subtype.exact), Sil.Tint Sil.IULong)] } + let exec_with_lvalue_as_reference f trans_state stmt = + let expr_info = match Clang_ast_proj.get_expr_tuple stmt with + | Some (_, _, ei) -> ei + | None -> assert false in + let res_trans = f trans_state stmt in + if expr_info.Clang_ast_t.ei_value_kind = `LValue then + let (exp, typ) = extract_exp_from_list res_trans.exps + "[Warning] Need exactly one expression to add reference type\n" in + { res_trans with exps = [(exp, Sil.Tptr (typ, Sil.Pk_reference))] } + else res_trans + (* Execute translation of e forcing to release priority (if it's not free) and then setting it back.*) (* This is used in conditional operators where we need to force the priority to be free for the *) (* computation of the expressions*) @@ -590,7 +601,8 @@ struct CTrans_utils.assign_default_params params_stmt None fun_exp_stmt ~is_cxx_method:false else [] in let res_trans_par = - let l = list_map (fun i -> exec_with_self_exception instruction trans_state_param i) params_stmt in + let instruction' = exec_with_self_exception (exec_with_lvalue_as_reference instruction) in + let l = list_map (instruction' trans_state_param) params_stmt in let rt = collect_res_trans (res_trans_callee :: l) in { rt with exps = list_tl rt.exps } in let sil_fe, is_cf_retain_release = CTrans_models.builtin_predefined_model fun_exp_stmt sil_fe in @@ -672,7 +684,8 @@ struct let trans_state_param = { trans_state_pri with parent_line_number = line_number; succ_nodes = [] } in let result_trans_params = - let l = list_map (exec_with_self_exception instruction trans_state_param) params_stmt in + let instruction' = exec_with_lvalue_as_reference instruction in + let l = list_map (exec_with_self_exception instruction' trans_state_param) params_stmt in (* this function will automatically merge 'this' argument with rest of arguments in 'l'*) let rt = collect_res_trans (result_trans_callee :: l) in { rt with exps = list_tl rt.exps } in @@ -726,7 +739,9 @@ struct with Self.SelfClassException class_name -> let obj_c_message_expr_info = Ast_expressions.make_obj_c_message_expr_info_class selector class_name in obj_c_message_expr_info, empty_res_trans) in - let l = list_map (fun i -> exec_with_self_exception instruction trans_state_param i) rest in + let instruction' = + exec_with_self_exception (exec_with_lvalue_as_reference instruction) in + let l = list_map (instruction' trans_state_param) rest in obj_c_message_expr_info, collect_res_trans (fst_res_trans :: l) | [] -> obj_c_message_expr_info, empty_res_trans) in let (class_type, _, _, _) = CMethod_trans.get_class_selector_instance context obj_c_message_expr_info res_trans_par.exps in @@ -1412,7 +1427,9 @@ struct (* if ie is a block the translation need to be done with the block special cases by exec_with_block_priority*) let res_trans_ie = let trans_state' = { trans_state_pri with succ_nodes = next_node; parent_line_number = line_number } in - exec_with_block_priority_exception (exec_with_self_exception instruction) trans_state' ie stmt_info in + let instruction' = + exec_with_self_exception (exec_with_lvalue_as_reference instruction) in + exec_with_block_priority_exception instruction' trans_state' ie stmt_info in let root_nodes = res_trans_ie.root_nodes in let leaf_nodes = res_trans_ie.leaf_nodes in let (sil_e1', ie_typ) = extract_exp_from_list res_trans_ie.exps diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/box.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/reference/box.cpp.dot index d11258665..801947a75 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/reference/box.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/box.cpp.dot @@ -3,7 +3,7 @@ digraph iCFG { 5 -> 4 ; -4 [label="4: DeclStmt \n *&r:int =&v [line 12]\n NULLIFY(&r,false); [line 12]\n " shape="box"] +4 [label="4: DeclStmt \n *&r:int &=&v [line 12]\n NULLIFY(&r,false); [line 12]\n " shape="box"] 4 -> 3 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/init.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/reference/init.cpp.dot index 0278b46ee..b9f03354e 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/reference/init.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/init.cpp.dot @@ -3,7 +3,7 @@ digraph iCFG { 15 -> 14 ; -14 [label="14: DeclStmt \n n$1=*&par:int * [line 24]\n *&d:int =n$1 [line 24]\n REMOVE_TEMPS(n$1); [line 24]\n NULLIFY(&d,false); [line 24]\n " shape="box"] +14 [label="14: DeclStmt \n n$1=*&par:int * [line 24]\n *&d:int &=n$1 [line 24]\n REMOVE_TEMPS(n$1); [line 24]\n NULLIFY(&d,false); [line 24]\n " shape="box"] 14 -> 13 ; @@ -22,7 +22,7 @@ digraph iCFG { 10 -> 9 ; -9 [label="9: DeclStmt \n *&d:int =&par [line 18]\n NULLIFY(&d,false); [line 18]\n " shape="box"] +9 [label="9: DeclStmt \n *&d:int &=&par [line 18]\n NULLIFY(&d,false); [line 18]\n " shape="box"] 9 -> 8 ; @@ -41,7 +41,7 @@ digraph iCFG { 5 -> 4 ; -4 [label="4: DeclStmt \n n$1=*&par:int & [line 12]\n *&d:int =n$1 [line 12]\n REMOVE_TEMPS(n$1); [line 12]\n NULLIFY(&d,false); [line 12]\n " shape="box"] +4 [label="4: DeclStmt \n n$1=*&par:int & [line 12]\n *&d:int &=n$1 [line 12]\n REMOVE_TEMPS(n$1); [line 12]\n NULLIFY(&d,false); [line 12]\n " shape="box"] 4 -> 3 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_struct_e2e.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_struct_e2e.cpp.dot index cf1e78788..045a3cd88 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_struct_e2e.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_struct_e2e.cpp.dot @@ -75,7 +75,7 @@ digraph iCFG { 98 -> 102 ; -97 [label="97: Call _fun_set_field_ref \n n$2=*&x:class X & [line 113]\n _fun_set_field_ref(n$2:class X ,1:int ) [line 113]\n REMOVE_TEMPS(n$2); [line 113]\n " shape="box"] +97 [label="97: Call _fun_set_field_ref \n n$2=*&x:class X & [line 113]\n _fun_set_field_ref(n$2:class X &,1:int ) [line 113]\n REMOVE_TEMPS(n$2); [line 113]\n " shape="box"] 97 -> 96 ; @@ -90,7 +90,7 @@ digraph iCFG { 94 -> 97 ; -93 [label="93: Call _fun_set_field_ref \n n$2=*&x:class X & [line 108]\n _fun_set_field_ref(n$2:class X ,0:int ) [line 108]\n REMOVE_TEMPS(n$2); [line 108]\n " shape="box"] +93 [label="93: Call _fun_set_field_ref \n n$2=*&x:class X & [line 108]\n _fun_set_field_ref(n$2:class X &,0:int ) [line 108]\n REMOVE_TEMPS(n$2); [line 108]\n " shape="box"] 93 -> 92 ; @@ -105,7 +105,7 @@ digraph iCFG { 90 -> 93 ; -89 [label="89: Call _fun_nonzero_ref \n n$2=*&x:class X & [line 103]\n _fun_nonzero_ref(n$2:class X ) [line 103]\n REMOVE_TEMPS(n$2); [line 103]\n " shape="box"] +89 [label="89: Call _fun_nonzero_ref \n n$2=*&x:class X & [line 103]\n _fun_nonzero_ref(n$2:class X &) [line 103]\n REMOVE_TEMPS(n$2); [line 103]\n " shape="box"] 89 -> 88 ; @@ -120,7 +120,7 @@ digraph iCFG { 86 -> 89 ; -85 [label="85: Call _fun_zero_ref \n n$2=*&x:class X & [line 98]\n _fun_zero_ref(n$2:class X ) [line 98]\n REMOVE_TEMPS(n$2); [line 98]\n " shape="box"] +85 [label="85: Call _fun_zero_ref \n n$2=*&x:class X & [line 98]\n _fun_zero_ref(n$2:class X &) [line 98]\n REMOVE_TEMPS(n$2); [line 98]\n " shape="box"] 85 -> 84 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot index 9cb8c64f6..d142b481c 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/reference_type_e2e.cpp.dot @@ -3,11 +3,11 @@ digraph iCFG { 40 -> 39 ; -39 [label="39: DeclStmt \n *&r:int =&a [line 48]\n " shape="box"] +39 [label="39: DeclStmt \n *&r:int &=&a [line 48]\n " shape="box"] 39 -> 38 ; -38 [label="38: Call _fun_zero_ref \n n$1=*&r:int & [line 49]\n _fun_zero_ref(n$1:int ) [line 49]\n REMOVE_TEMPS(n$1); [line 49]\n NULLIFY(&r,false); [line 49]\n " shape="box"] +38 [label="38: Call _fun_zero_ref \n n$1=*&r:int & [line 49]\n _fun_zero_ref(n$1:int &) [line 49]\n REMOVE_TEMPS(n$1); [line 49]\n NULLIFY(&r,false); [line 49]\n " shape="box"] 38 -> 37 ; @@ -26,7 +26,7 @@ digraph iCFG { 34 -> 33 ; -33 [label="33: Call _fun_zero_ref \n _fun_zero_ref(&a:int ) [line 42]\n " shape="box"] +33 [label="33: Call _fun_zero_ref \n _fun_zero_ref(&a:int &) [line 42]\n " shape="box"] 33 -> 32 ; @@ -45,7 +45,7 @@ digraph iCFG { 29 -> 28 ; -28 [label="28: DeclStmt \n *&r:int =&a [line 35]\n " shape="box"] +28 [label="28: DeclStmt \n *&r:int &=&a [line 35]\n " shape="box"] 28 -> 27 ; diff --git a/infer/tests/codetoanalyze/cpp/frontend/reference/unbox.cpp.dot b/infer/tests/codetoanalyze/cpp/frontend/reference/unbox.cpp.dot index 0b58dafa6..359104dca 100644 --- a/infer/tests/codetoanalyze/cpp/frontend/reference/unbox.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/frontend/reference/unbox.cpp.dot @@ -15,7 +15,7 @@ digraph iCFG { 20 -> 19 ; -19 [label="19: Call _fun_fun_r \n n$0=*&p:int * [line 33]\n n$1=_fun_fun_r(n$0:int ) [line 33]\n REMOVE_TEMPS(n$0,n$1); [line 33]\n NULLIFY(&p,false); [line 33]\n NULLIFY(&a,false); [line 33]\n APPLY_ABSTRACTION; [line 33]\n " shape="box"] +19 [label="19: Call _fun_fun_r \n n$0=*&p:int * [line 33]\n n$1=_fun_fun_r(n$0:int &) [line 33]\n REMOVE_TEMPS(n$0,n$1); [line 33]\n NULLIFY(&p,false); [line 33]\n NULLIFY(&a,false); [line 33]\n APPLY_ABSTRACTION; [line 33]\n " shape="box"] 19 -> 18 ; @@ -30,7 +30,7 @@ digraph iCFG { 16 -> 15 ; -15 [label="15: DeclStmt \n *&r:int =&a [line 19]\n " shape="box"] +15 [label="15: DeclStmt \n *&r:int &=&a [line 19]\n " shape="box"] 15 -> 14 ; @@ -42,7 +42,7 @@ digraph iCFG { 13 -> 12 ; -12 [label="12: Call _fun_fun_r \n n$0=*&r:int & [line 23]\n n$1=_fun_fun_r(n$0:int ) [line 23]\n REMOVE_TEMPS(n$0,n$1); [line 23]\n NULLIFY(&r,false); [line 23]\n NULLIFY(&a,false); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"] +12 [label="12: Call _fun_fun_r \n n$0=*&r:int & [line 23]\n n$1=_fun_fun_r(n$0:int &) [line 23]\n REMOVE_TEMPS(n$0,n$1); [line 23]\n NULLIFY(&r,false); [line 23]\n NULLIFY(&a,false); [line 23]\n APPLY_ABSTRACTION; [line 23]\n " shape="box"] 12 -> 11 ;