|
| 1 | +// |
| 2 | +// Author: Enrico Steffinlongo for Diffblue Ltd |
| 3 | +// |
| 4 | + |
| 5 | +#include <util/c_types.h> |
| 6 | +#include <util/config.h> |
| 7 | +#include <util/cprover_prefix.h> |
| 8 | +#include <util/expr.h> |
| 9 | +#include <util/string_constant.h> |
| 10 | + |
| 11 | +#include "c_typecheck_base.h" |
| 12 | +#include "expr2c.h" |
| 13 | + |
| 14 | +/// Function to typecheck a shadow memory field_declaration function. |
| 15 | +/// \return a symbol of the shadow memory function |
| 16 | +/// \throws invalid_source_file_exceptiont if the typecheck fails. |
| 17 | +static symbol_exprt typecheck_field_decl( |
| 18 | + const side_effect_expr_function_callt &expr, |
| 19 | + const irep_idt &identifier, |
| 20 | + const namespacet &ns) |
| 21 | +{ |
| 22 | + // Check correct number of arguments |
| 23 | + if(expr.arguments().size() != 2) |
| 24 | + { |
| 25 | + std::ostringstream error_message; |
| 26 | + error_message << identifier << " takes exactly 2 arguments, but " |
| 27 | + << expr.arguments().size() << " were provided"; |
| 28 | + throw invalid_source_file_exceptiont{ |
| 29 | + error_message.str(), expr.source_location()}; |
| 30 | + } |
| 31 | + |
| 32 | + const auto &arg1 = expr.arguments()[0]; |
| 33 | + if(!can_cast_expr<string_constantt>(arg1)) |
| 34 | + { |
| 35 | + // Can't declare a shadow memory field that has a name that is not a |
| 36 | + // constant string |
| 37 | + std::ostringstream error_message; |
| 38 | + error_message << identifier |
| 39 | + << " argument 1 must be string constant (literal), but (" |
| 40 | + << expr2c(arg1, ns) << ") has type `" |
| 41 | + << type2c(arg1.type(), ns) << '`'; |
| 42 | + throw invalid_source_file_exceptiont{ |
| 43 | + error_message.str(), expr.source_location()}; |
| 44 | + } |
| 45 | + |
| 46 | + // The second argument type must be either a boolean or a bitvector of size |
| 47 | + // less or equal to the size of a char. |
| 48 | + const auto &arg2 = expr.arguments()[1]; |
| 49 | + const auto &arg2_type = arg2.type(); |
| 50 | + const auto arg2_type_as_bv = |
| 51 | + type_try_dynamic_cast<bitvector_typet>(arg2_type); |
| 52 | + if( |
| 53 | + (!arg2_type_as_bv || |
| 54 | + arg2_type_as_bv->get_width() > config.ansi_c.char_width) && |
| 55 | + !can_cast_type<bool_typet>(arg2_type)) |
| 56 | + { |
| 57 | + // Can't declare a shadow memory field with a non-boolean and non bitvector |
| 58 | + // type or with a bitvector type greater than 8 bit. |
| 59 | + std::ostringstream error_message; |
| 60 | + error_message << identifier |
| 61 | + << " argument 2 must be a byte-sized integer, but (" |
| 62 | + << expr2c(arg2, ns) << ") has type `" << type2c(arg2_type, ns) |
| 63 | + << '`'; |
| 64 | + throw invalid_source_file_exceptiont{ |
| 65 | + error_message.str(), expr.source_location()}; |
| 66 | + } |
| 67 | + |
| 68 | + // The function type is just an ellipsis, otherwise the non-ellipsis arguments |
| 69 | + // will be typechecked and implicitly casted. |
| 70 | + code_typet t({}, void_type()); |
| 71 | + t.make_ellipsis(); |
| 72 | + |
| 73 | + // Create the symbol with the right type. |
| 74 | + symbol_exprt result(identifier, std::move(t)); |
| 75 | + result.add_source_location() = expr.source_location(); |
| 76 | + return result; |
| 77 | +} |
| 78 | + |
| 79 | +/// Function to typecheck a shadow memory get_field function. |
| 80 | +/// \return a symbol of the shadow memory function. |
| 81 | +/// \throws invalid_source_file_exceptiont if the typecheck fails. |
| 82 | +static symbol_exprt typecheck_get_field( |
| 83 | + const side_effect_expr_function_callt &expr, |
| 84 | + const irep_idt &identifier, |
| 85 | + const namespacet &ns) |
| 86 | +{ |
| 87 | + // Check correct number of arguments |
| 88 | + if(expr.arguments().size() != 2) |
| 89 | + { |
| 90 | + std::ostringstream error_message; |
| 91 | + error_message << identifier << " takes exactly 2 arguments, but " |
| 92 | + << expr.arguments().size() << " were provided"; |
| 93 | + throw invalid_source_file_exceptiont{ |
| 94 | + error_message.str(), expr.source_location()}; |
| 95 | + } |
| 96 | + |
| 97 | + const auto &arg1 = expr.arguments()[0]; |
| 98 | + const auto arg1_type_as_ptr = |
| 99 | + type_try_dynamic_cast<pointer_typet>(arg1.type()); |
| 100 | + if( |
| 101 | + !arg1_type_as_ptr || !arg1_type_as_ptr->has_subtype() || |
| 102 | + to_type_with_subtype(*arg1_type_as_ptr).subtype() == empty_typet{}) |
| 103 | + { |
| 104 | + // Can't get the shadow memory value of anything other than a non-void |
| 105 | + // pointer |
| 106 | + std::ostringstream error_message; |
| 107 | + error_message << identifier |
| 108 | + << " argument 1 must be a non-void pointer, but (" |
| 109 | + << expr2c(arg1, ns) << ") has type `" |
| 110 | + << type2c(arg1.type(), ns) |
| 111 | + << "`. Insert a cast to the type that you want to access."; |
| 112 | + throw invalid_source_file_exceptiont{ |
| 113 | + error_message.str(), expr.source_location()}; |
| 114 | + } |
| 115 | + |
| 116 | + const auto &arg2 = expr.arguments()[1]; |
| 117 | + if(!can_cast_expr<string_constantt>(arg2)) |
| 118 | + { |
| 119 | + // Can't get value from a shadow memory field that has a name that is |
| 120 | + // not a constant string |
| 121 | + std::ostringstream error_message; |
| 122 | + error_message << identifier |
| 123 | + << " argument 2 must be string constant (literal), but (" |
| 124 | + << expr2c(arg2, ns) << ") has type `" |
| 125 | + << type2c(arg2.type(), ns) << '`'; |
| 126 | + throw invalid_source_file_exceptiont{ |
| 127 | + error_message.str(), expr.source_location()}; |
| 128 | + } |
| 129 | + |
| 130 | + // The function type is just an ellipsis, otherwise the non-ellipsis arguments |
| 131 | + // will be typechecked and implicitly casted. |
| 132 | + // Setting the return type to `signed_int_type` otherwise it was creating |
| 133 | + // problem with signed-unsigned char comparison. Having `signed_int_type` |
| 134 | + // seems to fix the issue as it contains more bits for the conversion. |
| 135 | + code_typet t({}, signed_int_type()); |
| 136 | + t.make_ellipsis(); |
| 137 | + |
| 138 | + // Create the symbol with the right type. |
| 139 | + symbol_exprt result(identifier, std::move(t)); |
| 140 | + result.add_source_location() = expr.source_location(); |
| 141 | + return result; |
| 142 | +} |
| 143 | + |
| 144 | +/// Function to typecheck a shadow memory set_field function. |
| 145 | +/// \return a symbol of the shadow memory function. |
| 146 | +/// \throws invalid_source_file_exceptiont if the typecheck fails. |
| 147 | +static symbol_exprt typecheck_set_field( |
| 148 | + const side_effect_expr_function_callt &expr, |
| 149 | + const irep_idt &identifier, |
| 150 | + const namespacet &ns) |
| 151 | +{ |
| 152 | + // Check correct number of arguments |
| 153 | + if(expr.arguments().size() != 3) |
| 154 | + { |
| 155 | + std::ostringstream error_message; |
| 156 | + error_message << identifier << " takes exactly 3 arguments, but " |
| 157 | + << expr.arguments().size() << " were provided"; |
| 158 | + throw invalid_source_file_exceptiont{ |
| 159 | + error_message.str(), expr.source_location()}; |
| 160 | + } |
| 161 | + |
| 162 | + const auto &arg1 = expr.arguments()[0]; |
| 163 | + const auto arg1_type_as_ptr = |
| 164 | + type_try_dynamic_cast<pointer_typet>(arg1.type()); |
| 165 | + if( |
| 166 | + !arg1_type_as_ptr || !arg1_type_as_ptr->has_subtype() || |
| 167 | + to_type_with_subtype(*arg1_type_as_ptr).subtype() == empty_typet{}) |
| 168 | + { |
| 169 | + // Can't set the shadow memory value of anything other than a non-void |
| 170 | + // pointer |
| 171 | + std::ostringstream error_message; |
| 172 | + error_message << identifier |
| 173 | + << " argument 1 must be a non-void pointer, but (" |
| 174 | + << expr2c(arg1, ns) << ") has type `" |
| 175 | + << type2c(arg1.type(), ns) |
| 176 | + << "`. Insert a cast to the type that you want to access."; |
| 177 | + throw invalid_source_file_exceptiont{ |
| 178 | + error_message.str(), expr.source_location()}; |
| 179 | + } |
| 180 | + |
| 181 | + const auto &arg2 = expr.arguments()[1]; |
| 182 | + if(!can_cast_expr<string_constantt>(arg2)) |
| 183 | + { |
| 184 | + // Can't set value from a shadow memory field that has a name that is |
| 185 | + // not a constant string |
| 186 | + std::ostringstream error_message; |
| 187 | + error_message << identifier |
| 188 | + << " argument 2 must be string constant (literal), but (" |
| 189 | + << expr2c(arg2, ns) << ") has type `" |
| 190 | + << type2c(arg2.type(), ns) << '`'; |
| 191 | + throw invalid_source_file_exceptiont{ |
| 192 | + error_message.str(), expr.source_location()}; |
| 193 | + } |
| 194 | + |
| 195 | + // The third argument type must be either a boolean or a bitvector. |
| 196 | + const auto &arg3 = expr.arguments()[2]; |
| 197 | + const auto &arg3_type = arg3.type(); |
| 198 | + if( |
| 199 | + !can_cast_type<bitvector_typet>(arg3_type) && |
| 200 | + !can_cast_type<bool_typet>(arg3_type)) |
| 201 | + { |
| 202 | + // Can't set the shadow memory value with a non-boolean and non-bitvector |
| 203 | + // value. |
| 204 | + std::ostringstream error_message; |
| 205 | + error_message << identifier |
| 206 | + << " argument 3 must be a boolean or of integer value, but (" |
| 207 | + << expr2c(arg3, ns) << ") has type `" << type2c(arg3_type, ns) |
| 208 | + << '`'; |
| 209 | + throw invalid_source_file_exceptiont{ |
| 210 | + error_message.str(), expr.source_location()}; |
| 211 | + } |
| 212 | + |
| 213 | + // The function type is just an ellipsis, otherwise the non-ellipsis arguments |
| 214 | + // will be typechecked and implicitly casted. |
| 215 | + code_typet t({}, void_type()); |
| 216 | + t.make_ellipsis(); |
| 217 | + |
| 218 | + // Create the symbol with the right type. |
| 219 | + symbol_exprt result(identifier, std::move(t)); |
| 220 | + result.add_source_location() = expr.source_location(); |
| 221 | + return result; |
| 222 | +} |
| 223 | + |
| 224 | +/// Typecheck the function if it is a shadow_memory builtin and return a symbol |
| 225 | +/// for it. Otherwise return empty. |
| 226 | +optionalt<symbol_exprt> c_typecheck_baset::typecheck_shadow_memory_builtin( |
| 227 | + const side_effect_expr_function_callt &expr) |
| 228 | +{ |
| 229 | + const exprt &f_op = expr.function(); |
| 230 | + INVARIANT( |
| 231 | + can_cast_expr<symbol_exprt>(f_op), |
| 232 | + "expr.function() has to be a symbol_expr"); |
| 233 | + const irep_idt &identifier = to_symbol_expr(f_op).get_identifier(); |
| 234 | + |
| 235 | + if( |
| 236 | + identifier == CPROVER_PREFIX "field_decl_global" || |
| 237 | + identifier == CPROVER_PREFIX "field_decl_local") |
| 238 | + { |
| 239 | + return typecheck_field_decl(expr, identifier, *this); |
| 240 | + } |
| 241 | + else if(identifier == CPROVER_PREFIX "get_field") |
| 242 | + { |
| 243 | + return typecheck_get_field(expr, identifier, *this); |
| 244 | + } |
| 245 | + else if(identifier == CPROVER_PREFIX "set_field") |
| 246 | + { |
| 247 | + return typecheck_set_field(expr, identifier, *this); |
| 248 | + } |
| 249 | + // The function is not a shadow memory builtin, so we return <empty>. |
| 250 | + return {}; |
| 251 | +} |
0 commit comments