From 0a668c21618b59d2bcac27a5af83137e899fc6cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezgi=20=C3=87i=C3=A7ek?= Date: Tue, 7 Aug 2018 00:58:42 -0700 Subject: [PATCH] [DEAD_STORE] Capture `constexpr`s in lambdas Reviewed By: jvillard Differential Revision: D9179442 fbshipit-source-id: 8df2769c6 --- infer/src/checkers/liveness.ml | 24 ++++++++- .../cpp/liveness/dead_stores_constexpr.cpp | 53 +++++++++++++++++++ .../codetoanalyze/cpp/liveness/issues.exp | 2 + 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 infer/tests/codetoanalyze/cpp/liveness/dead_stores_constexpr.cpp diff --git a/infer/src/checkers/liveness.ml b/infer/src/checkers/liveness.ml index a2876bf55..14433b14e 100644 --- a/infer/src/checkers/liveness.ml +++ b/infer/src/checkers/liveness.ml @@ -50,7 +50,27 @@ module TransferFunctions (CFG : ProcCfg.S) = struct add_live_actuals_ actuals live_acc - let exec_instr astate _ _ = function + (* In C++14, for a lambda at block scope, constexpr variables in the + reaching scope may be used inside the lambda, even if they are + not captured. For further details: + https://timsong-cpp.github.io/cppwp/n4140/expr.prim.lambda#12 *) + let add_local_consts_for_lambdas pdesc call_exp astate = + let pname = Procdesc.get_proc_name pdesc in + match call_exp with + | Exp.Const (Cfun (Typ.Procname.ObjC_Cpp cpp_pname)) + when Typ.Procname.ObjC_Cpp.is_cpp_lambda cpp_pname -> + Procdesc.get_locals pdesc + |> List.fold ~init:astate ~f:(fun acc var_data -> + let open ProcAttributes in + if Typ.is_const var_data.typ.quals then + let const_local_var = Pvar.mk var_data.name pname |> Var.of_pvar in + Domain.add const_local_var acc + else acc ) + | _ -> + astate + + + let exec_instr astate {ProcData.pdesc} _ = function | Sil.Load (lhs_id, _, _, _) when Ident.is_none lhs_id -> (* dummy deref inserted by frontend--don't count as a read *) astate @@ -68,7 +88,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct exp_add_live exp astate | Sil.Call ((ret_id, _), call_exp, actuals, _, _) -> Domain.remove (Var.of_id ret_id) astate |> exp_add_live call_exp - |> add_live_actuals actuals call_exp + |> add_live_actuals actuals call_exp |> add_local_consts_for_lambdas pdesc call_exp | Sil.Declare_locals _ | Remove_temps _ | Abstract _ | Nullify _ -> astate diff --git a/infer/tests/codetoanalyze/cpp/liveness/dead_stores_constexpr.cpp b/infer/tests/codetoanalyze/cpp/liveness/dead_stores_constexpr.cpp new file mode 100644 index 000000000..672f0eab7 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/liveness/dead_stores_constexpr.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +int foo(int a); + +// before, we were raising DEAD_STORE for x +void capture_constexpr_good() { + constexpr int x = 10; + []() { + foo(x); + return; + }(); +} + +void call_it(void (*f)(void)) { f(); } + +void FP_capture_constexpr_good() { + constexpr int x = 1; + auto lambda = []() { + foo(x); + return; + }; + call_it(lambda); +} + +// we always assume const exprs are captured in lambdas +void FN_capture_constexpr_good() { + constexpr int x = 10; + []() { + foo(0); + return; + }(); +} + +void FN_init_capture_reassign_bad() { + constexpr int i = 1; // this is a dead store + return [i = 1]() { return i; }(); +} + +// expected DEAD_STORE +void capture_constexpr_bad() { + constexpr int x = 1; + foo(2); +} + +// expected since dead stores to a "sentinel" value are ignored +void capture_constexpr_sentinel_good() { + constexpr int x = 0; + []() { return; }(); +} diff --git a/infer/tests/codetoanalyze/cpp/liveness/issues.exp b/infer/tests/codetoanalyze/cpp/liveness/issues.exp index 2a8fd1d86..a1d027711 100644 --- a/infer/tests/codetoanalyze/cpp/liveness/issues.exp +++ b/infer/tests/codetoanalyze/cpp/liveness/issues.exp @@ -18,5 +18,7 @@ codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::plus_plus2_bad, 2, DEAD codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::plus_plus3_bad, 2, DEAD_STORE, no_bucket, ERROR, [Write of unused value] codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::reassign_param_bad, 0, DEAD_STORE, no_bucket, ERROR, [Write of unused value] codetoanalyze/cpp/liveness/dead_stores.cpp, dead_stores::use_then_dead_bad, 3, DEAD_STORE, no_bucket, ERROR, [Write of unused value] +codetoanalyze/cpp/liveness/dead_stores_constexpr.cpp, FP_capture_constexpr_good, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value] +codetoanalyze/cpp/liveness/dead_stores_constexpr.cpp, capture_constexpr_bad, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value] codetoanalyze/cpp/liveness/non_type_template_param.cpp, X<3>_isZeroBad, 1, DEAD_STORE, no_bucket, ERROR, [Write of unused value] codetoanalyze/cpp/liveness/non_type_template_param.cpp, instanciateTemplateBad, 3, DEAD_STORE, no_bucket, ERROR, [Write of unused value]