bool base_type_eqt::base_type_eq_rec( const exprt &expr1, const exprt &expr2) { if(expr1.id()!=expr2.id()) return false; if(!base_type_eq(expr1.type(), expr2.type())) return false; const exprt::operandst &expr1_op=expr1.operands(); const exprt::operandst &expr2_op=expr2.operands(); if(expr1_op.size()!=expr2_op.size()) return false; for(exprt::operandst::const_iterator it1=expr1_op.begin(), it2=expr2_op.begin(); it1!=expr1_op.end() && it2!=expr2_op.end(); ++it1, ++it2) if(!base_type_eq(*it1, *it2)) return false; if(expr1.id()==ID_constant) if(expr1.get(ID_value)!=expr2.get(ID_value)) return false; return true; }
literalt arrayst::record_array_equality( const equality_exprt &equality) { const exprt &op0=equality.op0(); const exprt &op1=equality.op1(); // check types if(!base_type_eq(op0.type(), op1.type(), ns)) { std::cout << equality.pretty() << std::endl; throw "record_array_equality got equality without matching types"; } assert(ns.follow(op0.type()).id()==ID_array); array_equalities.push_back(array_equalityt()); array_equalities.back().f1=op0; array_equalities.back().f2=op1; array_equalities.back().l=SUB::equality(op0, op1); arrays.make_union(op0, op1); collect_arrays(op0); collect_arrays(op1); return array_equalities.back().l; }
literalt boolbvt::convert_verilog_case_equality( const binary_relation_exprt &expr) { // This is 4-valued comparison, i.e., z===z, x===x etc. // The result is always Boolean. if(!base_type_eq(expr.lhs().type(), expr.rhs().type(), ns)) { std::cout << "######### lhs: " << expr.lhs().pretty() << '\n'; std::cout << "######### rhs: " << expr.rhs().pretty() << '\n'; throw "verilog_case_equality without matching types"; } const bvt &bv0=convert_bv(expr.lhs()); const bvt &bv1=convert_bv(expr.rhs()); if(bv0.size()!=bv1.size()) { std::cerr << "lhs: " << expr.lhs().pretty() << '\n'; std::cerr << "lhs size: " << bv0.size() << '\n'; std::cerr << "rhs: " << expr.rhs().pretty() << '\n'; std::cerr << "rhs size: " << bv1.size() << '\n'; throw "unexpected size mismatch on verilog_case_equality"; } if(expr.id()==ID_verilog_case_inequality) return !bv_utils.equal(bv0, bv1); else return bv_utils.equal(bv0, bv1); }
bvt boolbvt::convert_struct(const struct_exprt &expr) { const struct_typet &struct_type=to_struct_type(ns.follow(expr.type())); std::size_t width=boolbv_width(struct_type); const struct_typet::componentst &components=struct_type.components(); if(expr.operands().size()!=components.size()) { error().source_location=expr.find_source_location(); error() << "struct: wrong number of arguments" << eom; throw 0; } bvt bv; bv.resize(width); std::size_t offset=0, i=0; for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); it++) { const typet &subtype=it->type(); const exprt &op=expr.operands()[i]; if(!base_type_eq(subtype, op.type(), ns)) { error().source_location=expr.find_source_location(); error() << "struct: component type does not match: " << subtype.pretty() << " vs. " << op.type().pretty() << eom; throw 0; } std::size_t subtype_width=boolbv_width(subtype); if(subtype_width!=0) { const bvt &op_bv=convert_bv(op); assert(offset<width); assert(op_bv.size()==subtype_width); assert(offset+op_bv.size()<=width); for(std::size_t j=0; j<op_bv.size(); j++) bv[offset+j]=op_bv[j]; offset+=op_bv.size(); } i++; } assert(offset==width); return bv; }
bool base_type_eq( const exprt &expr1, const exprt &expr2, const namespacet &ns) { base_type_eqt base_type_eq(ns); return base_type_eq.base_type_eq(expr1, expr2); }
bool base_type_eq( const typet &type1, const typet &type2, const namespacet &ns) { base_type_eqt base_type_eq(ns); return base_type_eq.base_type_eq(type1, type2); }
void boolbvt::convert_with_struct( const struct_typet &type, const exprt &op1, const exprt &op2, const bvt &prev_bv, bvt &next_bv) { next_bv=prev_bv; const bvt &op2_bv=convert_bv(op2); const irep_idt &component_name=op1.get(ID_component_name); const struct_typet::componentst &components= type.components(); std::size_t offset=0; for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); it++) { const typet &subtype=it->type(); std::size_t sub_width=boolbv_width(subtype); if(it->get_name()==component_name) { if(!base_type_eq(subtype, op2.type(), ns)) { error().source_location=type.source_location(); error() << "with/struct: component `" << component_name << "' type does not match: " << subtype.pretty() << " vs. " << op2.type().pretty() << eom; throw 0; } if(sub_width!=op2_bv.size()) { error().source_location=type.source_location(); error() << "convert_with_struct: unexpected operand op2 width" << eom; throw 0; } for(std::size_t i=0; i<sub_width; i++) next_bv[offset+i]=op2_bv[i]; break; // done } offset+=sub_width; } }
void goto_symext::process_array_expr_rec( exprt &expr, const typet &type) const { if(expr.id()==ID_if) { if_exprt &if_expr=to_if_expr(expr); process_array_expr_rec(if_expr.true_case(), type); process_array_expr_rec(if_expr.false_case(), type); } else if(expr.id()==ID_index) { // strip index index_exprt &index_expr=to_index_expr(expr); exprt tmp=index_expr.array(); expr.swap(tmp); } else if(expr.id()==ID_typecast) { // strip exprt tmp=to_typecast_expr(expr).op0(); expr.swap(tmp); process_array_expr_rec(expr, type); } else if(expr.id()==ID_address_of) { // strip exprt tmp=to_address_of_expr(expr).op0(); expr.swap(tmp); process_array_expr_rec(expr, type); } else if(expr.id()==ID_symbol && expr.get_bool(ID_C_SSA_symbol) && to_ssa_expr(expr).get_original_expr().id()==ID_index) { const ssa_exprt &ssa=to_ssa_expr(expr); const index_exprt &index_expr=to_index_expr(ssa.get_original_expr()); exprt tmp=index_expr.array(); expr.swap(tmp); } else Forall_operands(it, expr) process_array_expr_rec(*it, it->type()); if(!base_type_eq(expr.type(), type, ns)) { byte_extract_exprt be(byte_extract_id()); be.type()=type; be.op()=expr; be.offset()=gen_zero(index_type()); expr.swap(be); } }
void linkingt::duplicate_type_symbol( symbolt &old_symbol, symbolt &new_symbol, bool &move) { // check if it is really the same // -- use base_type_eq, not linking_type_eq // first make sure that base_type_eq can soundly use ns/main_context only find_symbols_sett symbols; find_type_and_expr_symbols(new_symbol.type, symbols); bool ok=true; for(find_symbols_sett::const_iterator s_it=symbols.begin(); ok && s_it!=symbols.end(); s_it++) ok&=completed.find(*s_it)!=completed.end(); if(ok && base_type_eq(old_symbol.type, new_symbol.type, ns)) { move=false; return; } // they are different if(old_symbol.type.id()==ID_incomplete_struct && new_symbol.type.id()==ID_struct) { if(move) old_symbol.type=new_symbol.type; // store new type move=false; } else if(old_symbol.type.id()==ID_struct && new_symbol.type.id()==ID_incomplete_struct) { // ignore move=false; } else if(ns.follow(old_symbol.type).id()==ID_array && ns.follow(new_symbol.type).id()==ID_array) { if(move && to_array_type(ns.follow(old_symbol.type)).size().is_nil() && to_array_type(ns.follow(new_symbol.type)).size().is_not_nil()) old_symbol.type=new_symbol.type; // store new type move=false; } else { if(move) rename_type_symbol(new_symbol); move=true; } }
literalt boolbvt::convert_equality(const equal_exprt &expr) { if(!base_type_eq(expr.lhs().type(), expr.rhs().type(), ns)) { std::cout << "######### lhs: " << expr.lhs().pretty() << '\n'; std::cout << "######### rhs: " << expr.rhs().pretty() << '\n'; throw "equality without matching types"; } // see if it is an unbounded array if(is_unbounded_array(expr.lhs().type())) { // flatten byte_update/byte_extract operators if needed if(has_byte_operator(expr)) { exprt tmp=flatten_byte_operators(expr, ns); return record_array_equality(to_equal_expr(tmp)); } return record_array_equality(expr); } const bvt &bv0=convert_bv(expr.lhs()); const bvt &bv1=convert_bv(expr.rhs()); if(bv0.size()!=bv1.size()) { std::cerr << "lhs: " << expr.lhs().pretty() << '\n'; std::cerr << "lhs size: " << bv0.size() << '\n'; std::cerr << "rhs: " << expr.rhs().pretty() << '\n'; std::cerr << "rhs size: " << bv1.size() << '\n'; throw "unexpected size mismatch on equality"; } if(bv0.empty()) { // An empty bit-vector comparison. It's not clear // what this is meant to say. return prop.new_variable(); } return bv_utils.equal(bv0, bv1); }
bool dereferencet::type_compatible( const typet &object_type, const typet &dereference_type) const { if(dereference_type.id()==ID_empty) return true; // always ok if(base_type_eq(object_type, dereference_type, ns)) return true; // ok, they just match // check for struct prefixes if(object_type.id()==ID_struct && dereference_type.id()==ID_struct) { if(to_struct_type(dereference_type).is_prefix_of( to_struct_type(object_type))) return true; // ok, dreference_type is a prefix of object_type } // any code is ok if(dereference_type.id()==ID_code && object_type.id()==ID_code) return true; // bit vectors of same size are ok if((object_type.id()==ID_signedbv || object_type.id()==ID_unsignedbv) && (dereference_type.id()==ID_signedbv || dereference_type.id()==ID_unsignedbv)) { return object_type.get(ID_width)==dereference_type.get(ID_width); } // Any pointer to pointer is always ok, // but should likely check that width is the same. if(object_type.id()==ID_pointer && dereference_type.id()==ID_pointer) return true; // really different return false; }
bool value_set_dereferencet::dereference_type_compare( const typet &object_type, const typet &dereference_type) const { if(dereference_type.id()==ID_empty) return true; // always ok if(base_type_eq(object_type, dereference_type, ns)) return true; // ok, they just match // check for struct prefixes const typet ot_base=ns.follow(object_type), dt_base=ns.follow(dereference_type); if(ot_base.id()==ID_struct && dt_base.id()==ID_struct) { if(to_struct_type(dt_base).is_prefix_of( to_struct_type(ot_base))) return true; // ok, dt is a prefix of ot } // we are generous about code pointers if(dereference_type.id()==ID_code && object_type.id()==ID_code) return true; // bitvectors of same width are ok if((dereference_type.id()==ID_signedbv || dereference_type.id()==ID_unsignedbv) && (object_type.id()==ID_signedbv || object_type.id()==ID_unsignedbv) && to_bitvector_type(dereference_type).get_width()== to_bitvector_type(object_type).get_width()) return true; // really different return false; }
void arrayst::add_array_constraints_array_of( const index_sett &index_set, const array_of_exprt &expr) { // we got x=array_of[v] // get other array index applications // and add constraint x[i]=v for(index_sett::const_iterator it=index_set.begin(); it!=index_set.end(); it++) { index_exprt index_expr; index_expr.type()=ns.follow(expr.type()).subtype(); index_expr.array()=expr; index_expr.index()=*it; assert(base_type_eq(index_expr.type(), expr.op0().type(), ns)); // add constraint set_to_true(equality_exprt(index_expr, expr.op0())); } }
void boolbvt::convert_member(const member_exprt &expr, bvt &bv) { const exprt &struct_op=expr.struct_op(); const typet &struct_op_type=ns.follow(struct_op.type()); const bvt &struct_bv=convert_bv(struct_op); if(struct_op_type.id()==ID_union) { bv=convert_bv( byte_extract_exprt(byte_extract_id(), struct_op, gen_zero(integer_typet()), expr.type())); return; } else if(struct_op_type.id()==ID_struct) { const irep_idt &component_name=expr.get_component_name(); const struct_typet::componentst &components= to_struct_type(struct_op_type).components(); unsigned offset=0; for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); it++) { const typet &subtype=it->type(); unsigned sub_width=boolbv_width(subtype); if(it->get_name()==component_name) { if(!base_type_eq(subtype, expr.type(), ns)) { #if 0 std::cout << "DEBUG " << expr.pretty() << "\n"; #endif throw "member: component type does not match: "+ subtype.to_string()+" vs. "+ expr.type().to_string(); } bv.resize(sub_width); assert(offset+sub_width<=struct_bv.size()); for(unsigned i=0; i<sub_width; i++) bv[i]=struct_bv[offset+i]; return; } offset+=sub_width; } throw "component "+id2string(component_name)+" not found in structure"; } else throw "member takes struct or union operand"; }
void linkingt::duplicate_non_type_symbol( symbolt &old_symbol, symbolt &new_symbol) { // We first take care of file-local non-type symbols. // These are static functions, or static variables // inside function bodies. if(new_symbol.is_file_local || old_symbol.is_file_local) { // we just always rename these irep_idt old_identifier=new_symbol.name; irep_idt new_identifier=rename(old_identifier); replace_symbol.insert( old_identifier, symbol_exprt(new_identifier, new_symbol.type)); new_symbol.name=new_identifier; // move over! bool result=main_context.move(new_symbol); assert(!result); return; } // see if it is a function or a variable bool is_code_old_symbol=old_symbol.type.id()==ID_code; bool is_code_new_symbol=new_symbol.type.id()==ID_code; if(is_code_old_symbol!=is_code_new_symbol) { err_location(new_symbol.location); str << "error: conflicting definition for symbol \"" << old_symbol.display_name() << "\"" << std::endl; str << "old definition: " << to_string(old_symbol.type) << std::endl; str << "Module: " << old_symbol.module << std::endl; str << "new definition: " << to_string(new_symbol.type) << std::endl; str << "Module: " << new_symbol.module; throw 0; } if(is_code_old_symbol) { // Both are functions. // We don't compare the types, they will be too different; // we just care about the code if(!new_symbol.value.is_nil()) { if(old_symbol.value.is_nil()) { // the one with body wins! old_symbol.value=new_symbol.value; old_symbol.type=new_symbol.type; // for argument identifiers } else if(to_code_type(old_symbol.type).get_inlined()) { // ok } else if(base_type_eq(old_symbol.type, new_symbol.type, ns)) { // keep the one in old_symbol -- libraries come last! str << "warning: function `" << old_symbol.name << "' in module `" << new_symbol.module << "' is shadowed by a definition in module `" << old_symbol.module << "'"; warning(); } else { err_location(new_symbol.value); str << "error: duplicate definition of function `" << old_symbol.name << "'" << std::endl; str << "In module `" << old_symbol.module << "' and module `" << new_symbol.module << "'"; throw 0; } } } else { // both are variables if(!base_type_eq(old_symbol.type, new_symbol.type, ns)) { if(ns.follow(old_symbol.type).id()==ID_array && ns.follow(new_symbol.type).id()==ID_array) { if(to_array_type(ns.follow(old_symbol.type)).size().is_nil() && to_array_type(ns.follow(new_symbol.type)).size().is_not_nil()) old_symbol.type=new_symbol.type; // store new type } else if(ns.follow(old_symbol.type).id()==ID_pointer && ns.follow(new_symbol.type).id()==ID_array) { // store new type old_symbol.type=new_symbol.type; } else if(ns.follow(old_symbol.type).id()==ID_array && ns.follow(new_symbol.type).id()==ID_pointer) { // ignore } else if(ns.follow(old_symbol.type).id()==ID_pointer && ns.follow(new_symbol.type).id()==ID_pointer) { // ignore, generally ok } else if(old_symbol.type.id()==ID_incomplete_struct && new_symbol.type.id()==ID_struct) { // store new type old_symbol.type=new_symbol.type; } else if(old_symbol.type.id()==ID_struct && new_symbol.type.id()==ID_incomplete_struct) { // ignore } else { err_location(new_symbol.location); str << "error: conflicting definition for variable `" << old_symbol.name << "'" << std::endl; str << "old definition: " << to_string_verbose(old_symbol.type) << std::endl; str << "Module: " << old_symbol.module << std::endl; str << "new definition: " << to_string_verbose(new_symbol.type) << std::endl; str << "Module: " << new_symbol.module; throw 0; } } // care about initializers if(!new_symbol.value.is_nil() && !new_symbol.value.get_bool(ID_C_zero_initializer)) { if(old_symbol.value.is_nil() || old_symbol.value.get_bool(ID_C_zero_initializer)) { // new_symbol wins old_symbol.value=new_symbol.value; } else { // try simplifier exprt tmp_old=old_symbol.value, tmp_new=new_symbol.value; simplify(tmp_old, ns); simplify(tmp_new, ns); if(base_type_eq(tmp_old, tmp_new, ns)) { // ok, the same } else { err_location(new_symbol.value); str << "error: conflicting initializers for variable `" << old_symbol.name << "'" << std::endl; str << "old value: " << to_string(tmp_old) << std::endl; str << "Module: " << old_symbol.module << std::endl; str << "new value: " << to_string(tmp_new) << std::endl; str << "Module: " << new_symbol.module; throw 0; } } } } }
void goto_checkt::pointer_validity_check( const dereference_exprt &expr, const guardt &guard) { if(!enable_pointer_check) return; const exprt &pointer=expr.op0(); const typet &pointer_type=to_pointer_type(ns.follow(pointer.type())); assert(base_type_eq(pointer_type.subtype(), expr.type(), ns)); local_bitvector_analysist::flagst flags= local_bitvector_analysis->get(t, pointer); const typet &dereference_type=pointer_type.subtype(); // For Java, we only need to check for null if(mode==ID_java) { if(flags.is_unknown() || flags.is_null()) { notequal_exprt not_eq_null(pointer, gen_zero(pointer.type())); add_guarded_claim( not_eq_null, "reference is null", "pointer dereference", expr.find_source_location(), expr, guard); } } else { if(flags.is_unknown() || flags.is_null()) { add_guarded_claim( not_exprt(null_pointer(pointer)), "dereference failure: pointer NULL", "pointer dereference", expr.find_source_location(), expr, guard); } if(flags.is_unknown()) add_guarded_claim( not_exprt(invalid_pointer(pointer)), "dereference failure: pointer invalid", "pointer dereference", expr.find_source_location(), expr, guard); if(flags.is_uninitialized()) add_guarded_claim( not_exprt(invalid_pointer(pointer)), "dereference failure: pointer uninitialized", "pointer dereference", expr.find_source_location(), expr, guard); if(mode != ID_java) { if(flags.is_unknown() || flags.is_dynamic_heap()) add_guarded_claim( not_exprt(deallocated(pointer, ns)), "dereference failure: deallocated dynamic object", "pointer dereference", expr.find_source_location(), expr, guard); if(flags.is_unknown() || flags.is_dynamic_local()) add_guarded_claim( not_exprt(dead_object(pointer, ns)), "dereference failure: dead object", "pointer dereference", expr.find_source_location(), expr, guard); } if(enable_bounds_check) { if(flags.is_unknown() || flags.is_dynamic_heap()) { exprt dynamic_bounds= or_exprt(dynamic_object_lower_bound(pointer), dynamic_object_upper_bound(pointer, dereference_type, ns)); add_guarded_claim( implies_exprt(malloc_object(pointer, ns), not_exprt(dynamic_bounds)), "dereference failure: dynamic object bounds", "pointer dereference", expr.find_source_location(), expr, guard); } } if(enable_bounds_check) { if(flags.is_unknown() || flags.is_dynamic_local() || flags.is_static_lifetime()) { exprt object_bounds= or_exprt(object_lower_bound(pointer), object_upper_bound(pointer, dereference_type, ns)); add_guarded_claim( or_exprt(dynamic_object(pointer), not_exprt(object_bounds)), "dereference failure: object bounds", "pointer dereference", expr.find_source_location(), expr, guard); } } } }
void goto_inlinet::parameter_assignments( const locationt &location, const code_typet &code_type, const exprt::operandst &arguments, goto_programt &dest) { // iterates over the operands exprt::operandst::const_iterator it1=arguments.begin(); goto_programt::local_variablest local_variables; const code_typet::argumentst &argument_types= code_type.arguments(); // iterates over the types of the arguments for(code_typet::argumentst::const_iterator it2=argument_types.begin(); it2!=argument_types.end(); it2++) { // if you run out of actual arguments there was a mismatch if(it1==arguments.end()) { err_location(location); throw "function call: not enough arguments"; } const exprt &argument=static_cast<const exprt &>(*it2); // this is the type the n-th argument should be const typet &arg_type=ns.follow(argument.type()); const irep_idt &identifier=argument.cmt_identifier(); if(identifier=="") { err_location(location); throw "no identifier for function argument"; } { const symbolt &symbol=ns.lookup(identifier); goto_programt::targett decl=dest.add_instruction(); decl->make_other(); exprt tmp = code_declt(symbol_expr(symbol)); migrate_expr(tmp, decl->code); decl->location=location; decl->function=location.get_function(); decl->local_variables=local_variables; } local_variables.insert(identifier); // nil means "don't assign" if(it1->is_nil()) { } else { // this is the actual parameter exprt actual(*it1); // it should be the same exact type type2tc arg_type_2, actual_type_2; migrate_type(arg_type, arg_type_2); migrate_type(actual.type(), actual_type_2); if (!base_type_eq(arg_type_2, actual_type_2, ns)) { const typet &f_argtype = ns.follow(arg_type); const typet &f_acttype = ns.follow(actual.type()); // we are willing to do some conversion if((f_argtype.id()=="pointer" && f_acttype.id()=="pointer") || (f_argtype.is_array() && f_acttype.id()=="pointer" && f_argtype.subtype()==f_acttype.subtype())) { actual.make_typecast(arg_type); } else if((f_argtype.id()=="signedbv" || f_argtype.id()=="unsignedbv" || f_argtype.is_bool()) && (f_acttype.id()=="signedbv" || f_acttype.id()=="unsignedbv" || f_acttype.is_bool())) { actual.make_typecast(arg_type); } else { err_location(location); str << "function call: argument `" << identifier << "' type mismatch: got " << from_type(ns, identifier, it1->type()) << ", expected " << from_type(ns, identifier, arg_type); throw 0; } } // adds an assignment of the actual parameter to the formal parameter code_assignt assignment(symbol_exprt(identifier, arg_type), actual); assignment.location()=location; dest.add_instruction(ASSIGN); dest.instructions.back().location=location; migrate_expr(assignment, dest.instructions.back().code); dest.instructions.back().local_variables=local_variables; dest.instructions.back().function=location.get_function(); } it1++; } if(it1!=arguments.end()) { // too many arguments -- we just ignore that, no harm done } }
bool value_set_dereferencet::memory_model_bytes( exprt &value, const typet &to_type, const guardt &guard, const exprt &offset) { const typet from_type=value.type(); // We simply refuse to convert to/from code. if(from_type.id()==ID_code || to_type.id()==ID_code) return false; // We won't do this without a commitment to an endianness. if(config.ansi_c.endianness==configt::ansi_ct::endiannesst::NO_ENDIANNESS) return false; // But everything else we will try! // We just rely on byte_extract to do the job! exprt result; // See if we have an array of bytes already, // and we want something byte-sized. if(ns.follow(from_type).id()==ID_array && pointer_offset_size(ns.follow(from_type).subtype(), ns)==1 && pointer_offset_size(to_type, ns)==1 && is_a_bv_type(ns.follow(from_type).subtype()) && is_a_bv_type(to_type)) { // yes, can use 'index' result=index_exprt(value, offset, ns.follow(from_type).subtype()); // possibly need to convert if(!base_type_eq(result.type(), to_type, ns)) result.make_typecast(to_type); } else { // no, use 'byte_extract' result=exprt(byte_extract_id(), to_type); result.copy_to_operands(value, offset); } value=result; // are we within the bounds? if(options.get_bool_option("pointer-check")) { // upper bound { exprt from_width=size_of_expr(from_type, ns); INVARIANT( from_width.is_not_nil(), "unknown or invalid type size:\n"+from_type.pretty()); exprt to_width= to_type.id()==ID_empty? from_integer(0, size_type()):size_of_expr(to_type, ns); INVARIANT( to_width.is_not_nil(), "unknown or invalid type size:\n"+to_type.pretty()); INVARIANT( from_width.type()==to_width.type(), "type mismatch on result of size_of_expr"); minus_exprt bound(from_width, to_width); if(bound.type()!=offset.type()) bound.make_typecast(offset.type()); binary_relation_exprt offset_upper_bound(offset, ID_gt, bound); guardt tmp_guard(guard); tmp_guard.add(offset_upper_bound); dereference_callback.dereference_failure( "pointer dereference", "object upper bound", tmp_guard); } // lower bound is easy if(!offset.is_zero()) { binary_relation_exprt offset_lower_bound( offset, ID_lt, from_integer(0, offset.type())); guardt tmp_guard(guard); tmp_guard.add(offset_lower_bound); dereference_callback.dereference_failure( "pointer dereference", "object lower bound", tmp_guard); } } return true; }
void goto_checkt::goto_check(goto_functiont &goto_function) { { const symbolt *init_symbol; if(!ns.lookup(CPROVER_PREFIX "initialize", init_symbol)) mode=init_symbol->mode; } assertions.clear(); local_bitvector_analysist local_bitvector_analysis_obj(goto_function); local_bitvector_analysis=&local_bitvector_analysis_obj; goto_programt &goto_program=goto_function.body; Forall_goto_program_instructions(it, goto_program) { t=it; goto_programt::instructiont &i=*it; new_code.clear(); // we clear all recorded assertions if // 1) we want to generate all assertions or // 2) the instruction is a branch target if(retain_trivial || i.is_target()) assertions.clear(); check(i.guard); // magic ERROR label? for(optionst::value_listt::const_iterator l_it=error_labels.begin(); l_it!=error_labels.end(); l_it++) { if(std::find(i.labels.begin(), i.labels.end(), *l_it)!=i.labels.end()) { goto_program_instruction_typet type= enable_assert_to_assume?ASSUME:ASSERT; goto_programt::targett t=new_code.add_instruction(type); t->guard=false_exprt(); t->source_location=i.source_location; t->source_location.set_property_class("error label"); t->source_location.set_comment("error label "+*l_it); t->source_location.set("user-provided", true); } } if(i.is_other()) { const irep_idt &statement=i.code.get(ID_statement); if(statement==ID_expression) { check(i.code); } else if(statement==ID_printf) { forall_operands(it, i.code) check(*it); } } else if(i.is_assign()) { const code_assignt &code_assign=to_code_assign(i.code); check(code_assign.lhs()); check(code_assign.rhs()); // the LHS might invalidate any assertion invalidate(code_assign.lhs()); } else if(i.is_function_call()) { const code_function_callt &code_function_call= to_code_function_call(i.code); // for Java, need to check whether 'this' is null // on non-static method invocations if(mode==ID_java && enable_pointer_check && !code_function_call.arguments().empty() && code_function_call.function().type().id()==ID_code && to_code_type(code_function_call.function().type()).has_this()) { exprt pointer=code_function_call.arguments()[0]; local_bitvector_analysist::flagst flags= local_bitvector_analysis->get(t, pointer); if(flags.is_unknown() || flags.is_null()) { notequal_exprt not_eq_null(pointer, gen_zero(pointer.type())); add_guarded_claim( not_eq_null, "this is null on method invokation", "pointer dereference", i.source_location, pointer, guardt()); } } forall_operands(it, code_function_call) check(*it); // the call might invalidate any assertion assertions.clear(); } else if(i.is_return()) { if(i.code.operands().size()==1) { check(i.code.op0()); // the return value invalidate any assertion invalidate(i.code.op0()); } } else if(i.is_throw()) { if(i.code.get_statement()==ID_expression && i.code.operands().size()==1 && i.code.op0().operands().size()==1) { // must not throw NULL exprt pointer=i.code.op0().op0(); if(pointer.type().subtype().get(ID_identifier)!="java::java.lang.AssertionError") { notequal_exprt not_eq_null(pointer, gen_zero(pointer.type())); add_guarded_claim( not_eq_null, "throwing null", "pointer dereference", i.source_location, pointer, guardt()); } } // this has no successor assertions.clear(); } else if(i.is_assert()) { if(i.source_location.get_bool("user-provided") && i.source_location.get_property_class()!="error label" && !enable_assertions) i.type=SKIP; } else if(i.is_assume()) { if(!enable_assumptions) i.type=SKIP; } else if(i.is_dead()) { if(enable_pointer_check) { assert(i.code.operands().size()==1); const symbol_exprt &variable=to_symbol_expr(i.code.op0()); // is it dirty? if(local_bitvector_analysis->dirty(variable)) { // need to mark the dead variable as dead goto_programt::targett t=new_code.add_instruction(ASSIGN); exprt address_of_expr=address_of_exprt(variable); exprt lhs=ns.lookup(CPROVER_PREFIX "dead_object").symbol_expr(); if(!base_type_eq(lhs.type(), address_of_expr.type(), ns)) address_of_expr.make_typecast(lhs.type()); exprt rhs=if_exprt( side_effect_expr_nondett(bool_typet()), address_of_expr, lhs, lhs.type()); t->source_location=i.source_location; t->code=code_assignt(lhs, rhs); t->code.add_source_location()=i.source_location; } } } else if(i.is_end_function()) { if(i.function==goto_functionst::entry_point() && enable_memory_leak_check) { const symbolt &leak=ns.lookup(CPROVER_PREFIX "memory_leak"); const symbol_exprt leak_expr=leak.symbol_expr(); // add self-assignment to get helpful counterexample output goto_programt::targett t=new_code.add_instruction(); t->make_assignment(); t->code=code_assignt(leak_expr, leak_expr); source_locationt source_location; source_location.set_function(i.function); equal_exprt eq(leak_expr, gen_zero(ns.follow(leak.type))); add_guarded_claim( eq, "dynamically allocated memory never freed", "memory-leak", source_location, eq, guardt()); } } for(goto_programt::instructionst::iterator i_it=new_code.instructions.begin(); i_it!=new_code.instructions.end(); i_it++) { if(i_it->source_location.is_nil()) { i_it->source_location.id(irep_idt()); if(it->source_location.get_file()!=irep_idt()) i_it->source_location.set_file(it->source_location.get_file()); if(it->source_location.get_line()!=irep_idt()) i_it->source_location.set_line(it->source_location.get_line()); if(it->source_location.get_function()!=irep_idt()) i_it->source_location.set_function(it->source_location.get_function()); if(it->source_location.get_column()!=irep_idt()) i_it->source_location.set_column(it->source_location.get_column()); } if(i_it->function==irep_idt()) i_it->function=it->function; } // insert new instructions -- make sure targets are not moved while(!new_code.instructions.empty()) { goto_program.insert_before_swap(it, new_code.instructions.front()); new_code.instructions.pop_front(); it++; } }
void goto_inlinet::parameter_assignments( const source_locationt &source_location, const irep_idt &function_name, const code_typet &code_type, const exprt::operandst &arguments, goto_programt &dest) { // iterates over the operands exprt::operandst::const_iterator it1=arguments.begin(); const code_typet::parameterst ¶meter_types= code_type.parameters(); // iterates over the types of the parameters for(code_typet::parameterst::const_iterator it2=parameter_types.begin(); it2!=parameter_types.end(); it2++) { const code_typet::parametert ¶meter=*it2; // this is the type the n-th argument should be const typet &par_type=ns.follow(parameter.type()); const irep_idt &identifier=parameter.get_identifier(); if(identifier==irep_idt()) { error().source_location=source_location; error() << "no identifier for function parameter" << eom; throw 0; } { const symbolt &symbol=ns.lookup(identifier); goto_programt::targett decl=dest.add_instruction(); decl->make_decl(); decl->code=code_declt(symbol.symbol_expr()); decl->code.add_source_location()=source_location; decl->source_location=source_location; decl->function=function_name; } // this is the actual parameter exprt actual; // if you run out of actual arguments there was a mismatch if(it1==arguments.end()) { warning().source_location=source_location; warning() << "call to `" << function_name << "': " << "not enough arguments, " << "inserting non-deterministic value" << eom; actual=side_effect_expr_nondett(par_type); } else actual=*it1; // nil means "don't assign" if(actual.is_nil()) { } else { // it should be the same exact type as the parameter, // subject to some exceptions if(!base_type_eq(par_type, actual.type(), ns)) { const typet &f_partype = ns.follow(par_type); const typet &f_acttype = ns.follow(actual.type()); // we are willing to do some conversion if((f_partype.id()==ID_pointer && f_acttype.id()==ID_pointer) || (f_partype.id()==ID_pointer && f_acttype.id()==ID_array && f_partype.subtype()==f_acttype.subtype())) { actual.make_typecast(par_type); } else if((f_partype.id()==ID_signedbv || f_partype.id()==ID_unsignedbv || f_partype.id()==ID_bool) && (f_acttype.id()==ID_signedbv || f_acttype.id()==ID_unsignedbv || f_acttype.id()==ID_bool)) { actual.make_typecast(par_type); } else { error().source_location=actual.find_source_location(); error() << "function call: argument `" << identifier << "' type mismatch: argument is `" // << from_type(ns, identifier, actual.type()) << actual.type().pretty() << "', parameter is `" << from_type(ns, identifier, par_type) << "'" << eom; throw 0; } } // adds an assignment of the actual parameter to the formal parameter code_assignt assignment(symbol_exprt(identifier, par_type), actual); assignment.add_source_location()=source_location; dest.add_instruction(ASSIGN); dest.instructions.back().source_location=source_location; dest.instructions.back().code.swap(assignment); dest.instructions.back().function=function_name; } if(it1!=arguments.end()) ++it1; } if(it1!=arguments.end()) { // too many arguments -- we just ignore that, no harm done } }
value_set_dereferencet::valuet value_set_dereferencet::build_reference_to( const exprt &what, const modet mode, const exprt &pointer_expr, const guardt &guard) { const typet &dereference_type= ns.follow(pointer_expr.type()).subtype(); if(what.id()==ID_unknown || what.id()==ID_invalid) { invalid_pointer(pointer_expr, guard); return valuet(); } if(what.id()!=ID_object_descriptor) throw "unknown points-to: "+what.id_string(); const object_descriptor_exprt &o=to_object_descriptor_expr(what); const exprt &root_object=o.root_object(); const exprt &object=o.object(); #if 0 std::cout << "O: " << from_expr(ns, "", root_object) << '\n'; #endif valuet result; if(root_object.id()=="NULL-object") { if(options.get_bool_option("pointer-check")) { guardt tmp_guard(guard); if(o.offset().is_zero()) { tmp_guard.add(null_pointer(pointer_expr)); dereference_callback.dereference_failure( "pointer dereference", "NULL pointer", tmp_guard); } else { tmp_guard.add(null_object(pointer_expr)); dereference_callback.dereference_failure( "pointer dereference", "NULL plus offset pointer", tmp_guard); } } } else if(root_object.id()==ID_dynamic_object) { // const dynamic_object_exprt &dynamic_object= // to_dynamic_object_expr(root_object); // the object produced by malloc exprt malloc_object= ns.lookup(CPROVER_PREFIX "malloc_object").symbol_expr(); exprt is_malloc_object=same_object(pointer_expr, malloc_object); // constraint that it actually is a dynamic object exprt dynamic_object_expr(ID_dynamic_object, bool_typet()); dynamic_object_expr.copy_to_operands(pointer_expr); // this is also our guard result.pointer_guard=dynamic_object_expr; // can't remove here, turn into *p result.value=dereference_exprt(pointer_expr, dereference_type); if(options.get_bool_option("pointer-check")) { // if(!dynamic_object.valid().is_true()) { // check if it is still alive guardt tmp_guard(guard); tmp_guard.add(deallocated(pointer_expr, ns)); dereference_callback.dereference_failure( "pointer dereference", "dynamic object deallocated", tmp_guard); } if(options.get_bool_option("bounds-check")) { if(!o.offset().is_zero()) { // check lower bound guardt tmp_guard(guard); tmp_guard.add(is_malloc_object); tmp_guard.add( dynamic_object_lower_bound( pointer_expr, ns, nil_exprt())); dereference_callback.dereference_failure( "pointer dereference", "dynamic object lower bound", tmp_guard); } { // check upper bound // we check SAME_OBJECT(__CPROVER_malloc_object, p) && // POINTER_OFFSET(p)+size>__CPROVER_malloc_size guardt tmp_guard(guard); tmp_guard.add(is_malloc_object); tmp_guard.add( dynamic_object_upper_bound( pointer_expr, dereference_type, ns, size_of_expr(dereference_type, ns))); dereference_callback.dereference_failure( "pointer dereference", "dynamic object upper bound", tmp_guard); } } } } else if(root_object.id()==ID_integer_address) { // This is stuff like *((char *)5). // This is turned into an access to __CPROVER_memory[...]. if(language_mode==ID_java) { result.value=nil_exprt(); return result; } const symbolt &memory_symbol=ns.lookup(CPROVER_PREFIX "memory"); exprt symbol_expr=symbol_exprt(memory_symbol.name, memory_symbol.type); if(base_type_eq( ns.follow(memory_symbol.type).subtype(), dereference_type, ns)) { // Types match already, what a coincidence! // We can use an index expression. exprt index_expr=index_exprt(symbol_expr, pointer_offset(pointer_expr)); index_expr.type()=ns.follow(memory_symbol.type).subtype(); result.value=index_expr; } else if(dereference_type_compare( ns.follow(memory_symbol.type).subtype(), dereference_type)) { exprt index_expr=index_exprt(symbol_expr, pointer_offset(pointer_expr)); index_expr.type()=ns.follow(memory_symbol.type).subtype(); result.value=typecast_exprt(index_expr, dereference_type); } else { // We need to use byte_extract. // Won't do this without a commitment to an endianness. if(config.ansi_c.endianness==configt::ansi_ct::endiannesst::NO_ENDIANNESS) { } else { exprt byte_extract(byte_extract_id(), dereference_type); byte_extract.copy_to_operands( symbol_expr, pointer_offset(pointer_expr)); result.value=byte_extract; } } } else { // something generic -- really has to be a symbol address_of_exprt object_pointer(object); if(o.offset().is_zero()) { equal_exprt equality(pointer_expr, object_pointer); if(ns.follow(equality.lhs().type())!=ns.follow(equality.rhs().type())) equality.lhs().make_typecast(equality.rhs().type()); result.pointer_guard=equality; } else { result.pointer_guard=same_object(pointer_expr, object_pointer); } guardt tmp_guard(guard); tmp_guard.add(result.pointer_guard); valid_check(object, tmp_guard, mode); const typet &object_type=ns.follow(object.type()); const exprt &root_object=o.root_object(); const typet &root_object_type=ns.follow(root_object.type()); exprt root_object_subexpression=root_object; if(dereference_type_compare(object_type, dereference_type) && o.offset().is_zero()) { // The simplest case: types match, and offset is zero! // This is great, we are almost done. result.value=object; if(object_type!=ns.follow(dereference_type)) result.value.make_typecast(dereference_type); } else if(root_object_type.id()==ID_array && dereference_type_compare( root_object_type.subtype(), dereference_type)) { // We have an array with a subtype that matches // the dereferencing type. // We will require well-alignedness! exprt offset; // this should work as the object is essentially the root object if(o.offset().is_constant()) offset=o.offset(); else offset=pointer_offset(pointer_expr); exprt adjusted_offset; // are we doing a byte? mp_integer element_size= dereference_type.id()==ID_empty? pointer_offset_size(char_type(), ns): pointer_offset_size(dereference_type, ns); if(element_size==1) { // no need to adjust offset adjusted_offset=offset; } else if(element_size<=0) { throw "unknown or invalid type size of:\n"+dereference_type.pretty(); } else { exprt element_size_expr= from_integer(element_size, offset.type()); adjusted_offset=binary_exprt( offset, ID_div, element_size_expr, offset.type()); // TODO: need to assert well-alignedness } index_exprt index_expr= index_exprt(root_object, adjusted_offset, root_object_type.subtype()); bounds_check(index_expr, tmp_guard); result.value=index_expr; if(ns.follow(result.value.type())!=ns.follow(dereference_type)) result.value.make_typecast(dereference_type); } else if(get_subexpression_at_offset( root_object_subexpression, o.offset(), dereference_type, ns)) { // Successfully found a member, array index, or combination thereof // that matches the desired type and offset: result.value=root_object_subexpression; } else { // we extract something from the root object result.value=o.root_object(); // this is relative to the root object const exprt offset=pointer_offset(pointer_expr); if(memory_model(result.value, dereference_type, tmp_guard, offset)) { // ok, done } else { if(options.get_bool_option("pointer-check")) { std::string msg="memory model not applicable (got `"; msg+=from_type(ns, "", result.value.type()); msg+="', expected `"; msg+=from_type(ns, "", dereference_type); msg+="')"; dereference_callback.dereference_failure( "pointer dereference", msg, tmp_guard); } return valuet(); // give up, no way that this is ok } } } return result; }
void c_typecastt::implicit_typecast_followed( exprt &expr, const typet &src_type, const typet &dest_type) { if(dest_type.id()==ID_union) // do transparent union if(dest_type.id()==ID_union && dest_type.get_bool(ID_C_transparent_union) && src_type.id()!=ID_union) { // The argument corresponding to a transparent union type can be of any // type in the union; no explicit cast is required. // Check union members. const union_typet &dest_union_type=to_union_type(dest_type); for(union_typet::componentst::const_iterator it=dest_union_type.components().begin(); it!=dest_union_type.components().end(); it++) { if(!check_c_implicit_typecast(src_type, it->type())) { // build union constructor exprt union_expr(ID_union, dest_union_type); union_expr.move_to_operands(expr); union_expr.set(ID_component_name, it->get_name()); expr=union_expr; return; // ok } } } if(dest_type.id()==ID_pointer) { // special case: 0 == NULL if(expr.is_zero() && ( src_type.id()==ID_unsignedbv || src_type.id()==ID_signedbv || src_type.id()==ID_natural || src_type.id()==ID_integer)) { expr=exprt(ID_constant, dest_type); expr.set(ID_value, ID_NULL); return; // ok } if(src_type.id()==ID_pointer || src_type.id()==ID_array) { // we are quite generous about pointers const typet &src_sub=ns.follow(src_type.subtype()); const typet &dest_sub=ns.follow(dest_type.subtype()); if(is_void_pointer(src_type) || is_void_pointer(dest_type)) { // from/to void is always good } else if(src_sub.id()==ID_code && dest_sub.id()==ID_code) { // very generous: // between any two function pointers it's ok } else if(base_type_eq(src_type.subtype(), dest_type.subtype(), ns)) { // ok } else if((is_number(src_sub) || src_sub.id()==ID_c_enum) && (is_number(dest_sub) || dest_sub.id()==ID_c_enum)) { // Also generous: between any to scalar types it's ok. // We should probably check the size. } else warnings.push_back("incompatible pointer types"); // check qualifiers /* if(src_type.subtype().get_bool(ID_C_constant) && !dest_type.subtype().get_bool(ID_C_constant)) warnings.push_back("disregarding const"); */ if(src_type.subtype().get_bool(ID_C_volatile) && !dest_type.subtype().get_bool(ID_C_volatile)) warnings.push_back("disregarding volatile"); if(src_type==dest_type) { expr.type()=src_type; // because of qualifiers } else do_typecast(expr, dest_type); return; // ok } } if(check_c_implicit_typecast(src_type, dest_type)) errors.push_back("implicit conversion not permitted"); else if(src_type!=dest_type) do_typecast(expr, dest_type); }
void goto_symext::symex_other( const goto_functionst &goto_functions, statet &state) { const goto_programt::instructiont &instruction=*state.source.pc; const codet &code=to_code(instruction.code); const irep_idt &statement=code.get_statement(); if(statement==ID_expression) { // ignore } else if(statement==ID_cpp_delete || statement=="cpp_delete[]") { codet clean_code=code; clean_expr(clean_code, state, false); symex_cpp_delete(state, clean_code); } else if(statement==ID_free) { // ignore } else if(statement==ID_printf) { codet clean_code=code; clean_expr(clean_code, state, false); symex_printf(state, nil_exprt(), clean_code); } else if(statement==ID_input) { codet clean_code(code); clean_expr(clean_code, state, false); symex_input(state, clean_code); } else if(statement==ID_output) { codet clean_code(code); clean_expr(clean_code, state, false); symex_output(state, clean_code); } else if(statement==ID_decl) { assert(false); // see symex_decl.cpp } else if(statement==ID_nondet) { // like skip } else if(statement==ID_asm) { // we ignore this for now } else if(statement==ID_array_copy) { assert(code.operands().size()==2); codet clean_code(code); // we need to add dereferencing for both operands dereference_exprt d0, d1; d0.op0()=code.op0(); d0.type()=code.op0().type().subtype(); d1.op0()=code.op1(); d1.type()=code.op1().type().subtype(); clean_code.op0()=d0; clean_code.op1()=d1; clean_expr(clean_code, state, false); process_array_expr(clean_code.op0()); process_array_expr(clean_code.op1()); if(ns.follow(clean_code.op0().type()).id()!=ID_array) throw "array_copy expects array operand"; if(!base_type_eq(clean_code.op0().type(), clean_code.op1().type(), ns)) throw "array_copy expects matching array types"; code_assignt assignment; assignment.lhs()=clean_code.op0(); assignment.rhs()=clean_code.op1(); basic_symext::symex_assign(state, assignment); } else if(statement==ID_array_set) { assert(code.operands().size()==2); codet clean_code(code); // we need to add dereferencing for the first operand dereference_exprt d0; d0.op0()=code.op0(); d0.type()=code.op0().type().subtype(); clean_code.op0()=d0; clean_expr(clean_code, state, false); process_array_expr(clean_code.op0()); const typet &array_type=ns.follow(clean_code.op0().type()); if(array_type.id()!=ID_array) throw "array_set expects array operand"; if(!base_type_eq(array_type.subtype(), clean_code.op1().type(), ns)) clean_code.op1().make_typecast(array_type.subtype()); code_assignt assignment; assignment.lhs()=clean_code.op0(); assignment.rhs()=array_of_exprt(clean_code.op1(), clean_code.op0().type()); basic_symext::symex_assign(state, assignment); } else if(statement==ID_user_specified_predicate || statement==ID_user_specified_parameter_predicates || statement == ID_user_specified_return_predicates) { // like skip } else throw "unexpected statement: "+id2string(statement); }
void goto_symext::dereference_rec( exprt &expr, statet &state, guardt &guard, const bool write) { if(expr.id()==ID_dereference) { if(expr.operands().size()!=1) throw "dereference takes one operand"; exprt tmp1; tmp1.swap(expr.op0()); // first make sure there are no dereferences in there dereference_rec(tmp1, state, guard, false); // we need to set up some elaborate call-backs symex_dereference_statet symex_dereference_state(*this, state); value_set_dereferencet dereference( ns, new_symbol_table, options, symex_dereference_state, language_mode); // std::cout << "**** " << from_expr(ns, "", tmp1) << std::endl; exprt tmp2=dereference.dereference( tmp1, guard, write?value_set_dereferencet::WRITE:value_set_dereferencet::READ); //std::cout << "**** " << from_expr(ns, "", tmp2) << std::endl; expr.swap(tmp2); // this may yield a new auto-object trigger_auto_object(expr, state); } else if(expr.id()==ID_index && to_index_expr(expr).array().id()==ID_member && to_array_type(ns.follow(to_index_expr(expr).array().type())). size().is_zero()) { // This is an expression of the form x.a[i], // where a is a zero-sized array. This gets // re-written into *(&x.a+i) index_exprt index_expr=to_index_expr(expr); address_of_exprt address_of_expr(index_expr.array()); address_of_expr.type()=pointer_typet(expr.type()); dereference_exprt tmp; tmp.pointer()=plus_exprt(address_of_expr, index_expr.index()); tmp.type()=expr.type(); tmp.add_source_location()=expr.source_location(); // recursive call dereference_rec(tmp, state, guard, write); expr.swap(tmp); } else if(expr.id()==ID_index && to_index_expr(expr).array().type().id()==ID_pointer) { // old stuff, will go away assert(false); } else if(expr.id()==ID_address_of) { address_of_exprt &address_of_expr=to_address_of_expr(expr); exprt &object=address_of_expr.object(); const typet &expr_type=ns.follow(expr.type()); expr=address_arithmetic(object, state, guard, expr_type.subtype().id()==ID_array); } else if(expr.id()==ID_typecast) { exprt &tc_op=to_typecast_expr(expr).op(); // turn &array into &array[0] when casting to pointer-to-element-type if(tc_op.id()==ID_address_of && to_address_of_expr(tc_op).object().type().id()==ID_array && base_type_eq( expr.type(), pointer_typet(to_address_of_expr(tc_op).object().type().subtype()), ns)) { expr= address_of_exprt( index_exprt( to_address_of_expr(tc_op).object(), from_integer(0, index_type()))); dereference_rec(expr, state, guard, write); } else { dereference_rec(tc_op, state, guard, write); } } else { Forall_operands(it, expr) dereference_rec(*it, state, guard, write); } }
void arrayst::collect_arrays(const exprt &a) { const array_typet &array_type= to_array_type(ns.follow(a.type())); if(a.id()==ID_with) { if(a.operands().size()!=3) throw "with expected to have three operands"; // check types if(!base_type_eq(array_type, a.op0().type(), ns)) { std::cout << a.pretty() << std::endl; throw "collect_arrays got with without matching types"; } arrays.make_union(a, a.op0()); collect_arrays(a.op0()); // make sure this shows as an application index_exprt index_expr; index_expr.type()=array_type.subtype(); index_expr.array()=a.op0(); index_expr.index()=a.op1(); record_array_index(index_expr); } else if(a.id()==ID_if) { if(a.operands().size()!=3) throw "if expected to have three operands"; // check types if(!base_type_eq(array_type, a.op1().type(), ns)) { std::cout << a.pretty() << std::endl; throw "collect_arrays got if without matching types"; } // check types if(!base_type_eq(array_type, a.op2().type(), ns)) { std::cout << a.pretty() << std::endl; throw "collect_arrays got if without matching types"; } arrays.make_union(a, a.op1()); arrays.make_union(a, a.op2()); collect_arrays(a.op1()); collect_arrays(a.op2()); } else if(a.id()==ID_symbol) { } else if(a.id()==ID_nondet_symbol) { } else if(a.id()==ID_member) { if(to_member_expr(a).struct_op().id()!=ID_symbol) throw "unexpected array expression: member with `"+a.op0().id_string()+"'"; } else if(a.id()==ID_constant || a.id()==ID_array || a.id()==ID_string_constant) { } else if(a.id()==ID_array_of) { } else throw "unexpected array expression: `"+a.id_string()+"'"; }
exprt dereferencet::read_object( const exprt &object, const exprt &offset, const typet &type) { const typet &object_type=ns.follow(object.type()); const typet &dest_type=ns.follow(type); // is the object an array with matching subtype? exprt simplified_offset=simplify_expr(offset, ns); // check if offset is zero if(simplified_offset.is_zero()) { // check type if(base_type_eq(object_type, dest_type, ns)) { return object; // trivial case } else if(type_compatible(object_type, dest_type)) { // the type differs, but we can do this with a typecast return typecast_exprt(object, dest_type); } } if(object.id()==ID_index) { const index_exprt &index_expr=to_index_expr(object); exprt index=index_expr.index(); // multiply index by object size exprt size=size_of_expr(object_type, ns); if(size.is_nil()) throw "dereference failed to get object size for index"; index.make_typecast(simplified_offset.type()); size.make_typecast(index.type()); exprt new_offset=plus_exprt(simplified_offset, mult_exprt(index, size)); return read_object(index_expr.array(), new_offset, type); } else if(object.id()==ID_member) { const member_exprt &member_expr=to_member_expr(object); const typet &compound_type= ns.follow(member_expr.struct_op().type()); if(compound_type.id()==ID_struct) { const struct_typet &struct_type= to_struct_type(compound_type); exprt member_offset=member_offset_expr( struct_type, member_expr.get_component_name(), ns); if(member_offset.is_nil()) throw "dereference failed to get member offset"; member_offset.make_typecast(simplified_offset.type()); exprt new_offset=plus_exprt(simplified_offset, member_offset); return read_object(member_expr.struct_op(), new_offset, type); } else if(compound_type.id()==ID_union) { // Unions are easy: the offset is always zero, // so simply pass down. return read_object(member_expr.struct_op(), offset, type); } } // check if we have an array with the right subtype if(object_type.id()==ID_array && base_type_eq(object_type.subtype(), dest_type, ns)) { // check proper alignment exprt size=size_of_expr(dest_type, ns); if(size.is_not_nil()) { mp_integer size_constant, offset_constant; if(!to_integer(simplify_expr(size, ns), size_constant) && !to_integer(simplified_offset, offset_constant) && (offset_constant%size_constant)==0) { // Yes! Can use index expression! mp_integer index_constant=offset_constant/size_constant; exprt index_expr=from_integer(index_constant, size.type()); return index_exprt(object, index_expr, dest_type); } } } // give up and use byte_extract return binary_exprt(object, byte_extract_id(), simplified_offset, dest_type); }
bool ssa_may_alias( const exprt &e1, const exprt &e2, const namespacet &ns) { #ifdef DEBUG std::cout << "MAY ALIAS1 " << from_expr(ns, "", e1) << " " << from_expr(ns, "", e2) << "\n"; #endif // The same? if(e1==e2) return true; // Both symbol? if(e1.id()==ID_symbol && e2.id()==ID_symbol) { return to_symbol_expr(e1).get_identifier()== to_symbol_expr(e2).get_identifier(); } // __CPROVER symbols if(e1.id()==ID_symbol && has_prefix( id2string(to_symbol_expr(e1).get_identifier()), CPROVER_PREFIX)) return false; if(e2.id()==ID_symbol && has_prefix( id2string(to_symbol_expr(e2).get_identifier()), CPROVER_PREFIX)) return false; if(e1.id()==ID_symbol && has_suffix( id2string(to_symbol_expr(e1).get_identifier()), "#return_value")) return false; if(e2.id()==ID_symbol && has_suffix( id2string(to_symbol_expr(e2).get_identifier()), "#return_value")) return false; // Both member? if(e1.id()==ID_member && e2.id()==ID_member) { const member_exprt &m1=to_member_expr(e1); const member_exprt &m2=to_member_expr(e2); // same component? if(m1.get_component_name()!=m2.get_component_name()) return false; return ssa_may_alias(m1.struct_op(), m2.struct_op(), ns); } // Both index? if(e1.id()==ID_index && e2.id()==ID_index) { const index_exprt &i1=to_index_expr(e1); const index_exprt &i2=to_index_expr(e2); return ssa_may_alias(i1.array(), i2.array(), ns); } const typet &t1=ns.follow(e1.type()); const typet &t2=ns.follow(e2.type()); // If one is an array and the other not, consider the elements if(t1.id()==ID_array && t2.id()!=ID_array) if(ssa_may_alias( index_exprt(e1, gen_zero(index_type()), t1.subtype()), e2, ns)) return true; if(t2.id()==ID_array && t2.id()!=ID_array) if(ssa_may_alias( e1, index_exprt(e2, gen_zero(index_type()), t2.subtype()), ns)) return true; // Pointers only alias with other pointers, // which is a restriction. if(t1.id()==ID_pointer) return t2.id()==ID_pointer; if(t2.id()==ID_pointer) return t1.id()==ID_pointer; // Is one a scalar pointer? if(e1.id()==ID_dereference && (t1.id()==ID_signedbv || t1.id()==ID_unsignedbv || t1.id()==ID_floatbv)) return true; if(e2.id()==ID_dereference && (t2.id()==ID_signedbv || t2.id()==ID_unsignedbv || t1.id()==ID_floatbv)) return true; // Is one a pointer? if(e1.id()==ID_dereference || e2.id()==ID_dereference) { // look at the types // same type? if(base_type_eq(t1, t2, ns)) { return true; } // should consider further options, e.g., struct prefixes return false; } return false; // both different objects }
exprt goto_symext::address_arithmetic( const exprt &expr, statet &state, guardt &guard, bool keep_array) { exprt result; if(expr.id()==ID_byte_extract_little_endian || expr.id()==ID_byte_extract_big_endian) { // address_of(byte_extract(op, offset, t)) is // address_of(op) + offset with adjustments for arrays const byte_extract_exprt &be=to_byte_extract_expr(expr); // recursive call result=address_arithmetic(be.op(), state, guard, keep_array); if(ns.follow(be.op().type()).id()==ID_array && result.id()==ID_address_of) { address_of_exprt &a=to_address_of_expr(result); // turn &a of type T[i][j] into &(a[0][0]) for(const typet *t=&(ns.follow(a.type().subtype())); t->id()==ID_array && !base_type_eq(expr.type(), *t, ns); t=&(ns.follow(*t).subtype())) a.object()=index_exprt(a.object(), from_integer(0, index_type())); } // do (expr.type() *)(((char *)op)+offset) result=typecast_exprt(result, pointer_typet(char_type())); // there could be further dereferencing in the offset exprt offset=be.offset(); dereference_rec(offset, state, guard, false); result=plus_exprt(result, offset); // treat &array as &array[0] const typet &expr_type=ns.follow(expr.type()); pointer_typet dest_type; if(expr_type.id()==ID_array && !keep_array) dest_type.subtype()=expr_type.subtype(); else dest_type.subtype()=expr_type; result=typecast_exprt(result, dest_type); } else if(expr.id()==ID_index || expr.id()==ID_member) { object_descriptor_exprt ode; ode.build(expr, ns); byte_extract_exprt be(byte_extract_id()); be.type()=expr.type(); be.op()=ode.root_object(); be.offset()=ode.offset(); // recursive call result=address_arithmetic(be, state, guard, keep_array); do_simplify(result); } else if(expr.id()==ID_dereference) { // ANSI-C guarantees &*p == p no matter what p is, // even if it's complete garbage // just grab the pointer, but be wary of further dereferencing // in the pointer itself result=to_dereference_expr(expr).pointer(); dereference_rec(result, state, guard, false); } else if(expr.id()==ID_if) { if_exprt if_expr=to_if_expr(expr); // the condition is not an address dereference_rec(if_expr.cond(), state, guard, false); // recursive call if_expr.true_case()= address_arithmetic(if_expr.true_case(), state, guard, keep_array); if_expr.false_case()= address_arithmetic(if_expr.false_case(), state, guard, keep_array); result=if_expr; } else if(expr.id()==ID_symbol || expr.id()==ID_string_constant || expr.id()==ID_label || expr.id()==ID_array) { // give up, just dereference result=expr; dereference_rec(result, state, guard, false); // turn &array into &array[0] if(ns.follow(result.type()).id()==ID_array && !keep_array) result=index_exprt(result, from_integer(0, index_type())); // handle field-sensitive SSA symbol mp_integer offset=0; if(expr.id()==ID_symbol && expr.get_bool(ID_C_SSA_symbol)) { offset=compute_pointer_offset(expr, ns); assert(offset>=0); } if(offset>0) { byte_extract_exprt be(byte_extract_id()); be.type()=expr.type(); be.op()=to_ssa_expr(expr).get_l1_object(); be.offset()=from_integer(offset, index_type()); result=address_arithmetic(be, state, guard, keep_array); do_simplify(result); } else result=address_of_exprt(result); } else throw "goto_symext::address_arithmetic does not handle "+expr.id_string(); const typet &expr_type=ns.follow(expr.type()); assert((expr_type.id()==ID_array && !keep_array) || base_type_eq(pointer_typet(expr_type), result.type(), ns)); return result; }