let get_bitwise_shift_assertion
    ~simplify_constants:simplify_constants
    ~warning:warning
    exp shiftop loperand roperand =
  (* - (1) right operand should be nonnegative and
     strictly less than the width of promoted left operand:
     now done by get_bitwise_shift_right_operand_assertion
     - (2) (since signed version) left operand should be nonnegative
     (implementation defined for right shift, undefined for left shift)
     - (3) (for signed left shift) (left operand should be nonnegative: see (2), and)
     result should be representable in result type *)

  let t = Cil.typeOf exp in
  let size = bitsSizeOf t in
  let () =
    if not(size = bitsSizeOf (Cil.typeOf loperand) && size <= 64)
      (* size of result type should be size of left (promoted) operand *)
    then (
      rte_warn "problem with bitsSize of %a: not treated" d_exp exp ;
    )
  in
  let maxValResult = (* compute greatest representable "size bits" (signed) integer *)
    get_signed_max size
(*
    let max64 = Int64.max_int
    and shiftright_value = 64 - size
    in if shiftright_value > 0 then Int64.shift_right max64 shiftright_value else max64
*)

  in let assertion_2 () =
      let term = translate_C_expr_to_term loperand
      in Logic_const.prel (Rge, term, Cil.lzero ())
     and assertion_3 () =
      let term = translate_C_expr_to_term ~cast:false exp
      in (* signed result is representable in result type if loperand times 2^roperand
            (where loperand and roperand are nonnegative),
            which should be equal to term (obtained with a shift),
            is less than the maximal value for the result type *)

        (* no cast to int since we check "true result" which is an integer*)
        Logic_const.prel (Rle, term, Cil.lconstant maxValResult)
  in let problem_with_assertion_2 () =
      if simplify_constants then (
        match get_expr_val loperand with
          | None -> (false,false)
          | Some c64 ->
              (* left operand is constant: check it is nonnegative *)
              if (Int64.compare c64 Int64.zero >= 0)
              then (truetrue(* constant operator and assertion holds *)
              else (true,false(* constant operator and assertion does not hold *)
      ) else (false,false)
     and problem_with_assertion_3 () =
      if simplify_constants then (
        match get_expr_val loperand, get_expr_val roperand with
          | None,_
          | _, None -> (false,false)
          | Some lval64, Some rval64 ->
              (* both operands are constant:
                 check result is representable in result type *)

              if (Int64.compare lval64 Int64.zero <= 0) ||
                (Int64.compare rval64 (Int64.of_int 64) >= 0) then
                (true,false(* constant operators and assertion does not hold *)
              else
                let result_true_val =
                  Int.shift_left (Int.of_int64 lval64) (Int.of_int64 rval64)
                in
                  if Int.compare result_true_val (Int.of_int64 maxValResult) > 0
                  then (true,false(* constant operators and assertion does not hold *)
                  else (true,true(* constant operators and assertion holds *)
      ) else (false,false)
  in let proceed_with_assertion_3 lassert =
      if (shiftop = Shiftltthen (
        match problem_with_assertion_3 () with
          | (true,false->
              let assertion = assertion_3 () in
                if warning then (
                  rte_warn
                    "shift assert broken (signed overflow): %a"
                    d_predicate_named assertion
                )
                ;
                ((assertion, Some (make_check_false ())))::lassert
          | (true,true)  -> lassert
          | (false,_)    -> (assertion_3 (), None)::lassert
      ) else lassert
  in
    match problem_with_assertion_2 () with
      | (true,false->
          let assertion = assertion_2 () in
            if warning then (
              rte_warn
                "shift assert broken (left operand should be nonnegative): %a"
                d_predicate_named assertion
            )
            ;
            (* do not proceed with assertion 3: left operand is negative,
               hence result is implementation defined anyway for left shift *)

            [ (assertion, Some (make_check_false ())) ]
      | (true,true)  -> proceed_with_assertion_3 [ ]
      | (false,_)    -> proceed_with_assertion_3 [ (assertion_2 (), None) ]