/// does not replace object in address_of expressions bool replace_symbol_extt::replace(exprt &dest) const { bool result=true; // first look at type if(have_to_replace(dest.type())) if(!replace_symbolt::replace(dest.type())) result=false; // now do expression itself if(!have_to_replace(dest)) return result; // do not replace object in address_of expressions if(dest.id()==ID_address_of) { const exprt &object = to_address_of_expr(dest).object(); if(object.id()==ID_symbol) { expr_mapt::const_iterator it= expr_map.find(object.get(ID_identifier)); if(it!=expr_map.end()) return false; } } else if(dest.id()==ID_symbol) { expr_mapt::const_iterator it= expr_map.find(dest.get(ID_identifier)); if(it!=expr_map.end()) { dest=it->second; return false; } } Forall_operands(it, dest) if(!replace(*it)) result=false; const irept &c_sizeof_type=dest.find(ID_C_c_sizeof_type); if(c_sizeof_type.is_not_nil() && !replace_symbolt::replace( static_cast<typet&>(dest.add(ID_C_c_sizeof_type)))) result=false; const irept &va_arg_type=dest.find(ID_C_va_arg_type); if(va_arg_type.is_not_nil() && !replace_symbolt::replace(static_cast<typet&>(dest.add(ID_C_va_arg_type)))) result=false; return result; }
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); } }
exprt dereferencet::dereference_rec( const exprt &address, const exprt &offset, const typet &type) { if(address.id()==ID_address_of) { const address_of_exprt &address_of_expr=to_address_of_expr(address); const exprt &object=address_of_expr.object(); return read_object(object, offset, type); } else if(address.id()==ID_typecast) { const typecast_exprt &typecast_expr=to_typecast_expr(address); return dereference_typecast(typecast_expr, offset, type); } else if(address.id()==ID_plus) { // pointer arithmetic if(address.operands().size()<2) throw "plus with less than two operands"; return dereference_plus(address, offset, type); } else if(address.id()==ID_if) { const if_exprt &if_expr=to_if_expr(address); return dereference_if(if_expr, offset, type); } else if(address.id()==ID_constant) { const typet result_type=ns.follow(address.type()).subtype(); // pointer-typed constant if(to_constant_expr(address).get_value()==ID_NULL) // NULL { // we turn this into (type *)0 exprt zero=gen_zero(index_type()); return dereference_rec( typecast_exprt(zero, address.type()), offset, type); } else throw "dereferencet: unexpected pointer constant "+address.pretty(); } else { throw "failed to dereference `"+address.id_string()+"'"; } }
void goto_symext::process_array_expr(exprt &expr) { // This may change the type of the expression! if(expr.id()==ID_if) { if_exprt &if_expr=to_if_expr(expr); process_array_expr(if_expr.true_case()); process_array_expr_rec(if_expr.false_case(), if_expr.true_case().type()); if_expr.type()=if_expr.true_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(expr); } else if(expr.id()==ID_address_of) { // strip exprt tmp=to_address_of_expr(expr).op0(); expr.swap(tmp); process_array_expr(expr); } 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(*it); }
exprt dereference_rec( const exprt &src, const ssa_value_domaint &ssa_value_domain, const std::string &nondet_prefix, const namespacet &ns) { if(src.id()==ID_dereference) { const exprt &pointer=dereference_rec( to_dereference_expr(src).pointer(), ssa_value_domain, nondet_prefix, ns); const typet &pointed_type=ns.follow(pointer.type().subtype()); const ssa_value_domaint::valuest values=ssa_value_domain(pointer, ns); exprt result; if(values.value_set.empty()) { result=pointed_object(pointer, ns); } else { auto it=values.value_set.begin(); if(values.null || values.unknown || (values.value_set.size()>1 && it->type().get_bool("#dynamic"))) { std::string dyn_type_name=pointed_type.id_string(); if(pointed_type.id()==ID_struct) dyn_type_name+="_"+id2string(to_struct_type(pointed_type).get_tag()); irep_idt identifier="ssa::"+dyn_type_name+"_obj$unknown"; result=symbol_exprt(identifier, src.type()); result.set("#unknown_obj", true); } else { result=ssa_alias_value(src, (it++)->get_expr(), ns); result.set("#heap_access", result.type().get_bool("#dynamic")); } for(; it!=values.value_set.end(); ++it) { exprt guard=ssa_alias_guard(src, it->get_expr(), ns); exprt value=ssa_alias_value(src, it->get_expr(), ns); result=if_exprt(guard, value, result); result.set( "#heap_access", result.get_bool("#heap_access") || value.type().get_bool("#dynamic")); } } return result; } else if(src.id()==ID_member) { member_exprt tmp=to_member_expr(src); tmp.struct_op()= dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); tmp.set("#heap_access", tmp.struct_op().get_bool("#heap_access")); #ifdef DEBUG std::cout << "dereference_rec tmp: " << from_expr(ns, "", tmp) << '\n'; #endif if(tmp.struct_op().is_nil()) return nil_exprt(); return lift_if(tmp); } else if(src.id()==ID_address_of) { address_of_exprt tmp=to_address_of_expr(src); tmp.object()= dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); tmp.set("#heap_access", tmp.object().get_bool("#heap_access")); if(tmp.object().is_nil()) return nil_exprt(); return lift_if(tmp); } else { exprt tmp=src; Forall_operands(it, tmp) { *it=dereference_rec(*it, ssa_value_domain, nondet_prefix, ns); if(it->get_bool("#heap_access")) tmp.set("#heap_access", true); } return tmp; }
exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const { if(expr.id()==ID_side_effect) { throw "unexpected side effect in read_rhs_rec"; } else if(expr.id()==ID_address_of) { address_of_exprt tmp=to_address_of_expr(expr); tmp.object()=read_rhs_address_of_rec(tmp.object(), loc); return address_canonizer(tmp, ns); } else if(expr.id()==ID_dereference) { throw "unexpected dereference in read_rhs_rec"; } else if(expr.id()==ID_index) { const index_exprt &index_expr=to_index_expr(expr); return index_exprt(read_rhs(index_expr.array(), loc), read_rhs(index_expr.index(), loc), expr.type()); } ssa_objectt object(expr, ns); // is it an object identifier? if(!object) { exprt tmp=expr; // copy Forall_operands(it, tmp) *it=read_rhs(*it, loc); return tmp; } // Argument is a struct-typed ssa object? // May need to split up into members. const typet &type=ns.follow(expr.type()); if(type.id()==ID_struct) { // build struct constructor struct_exprt result(expr.type()); const struct_typet &struct_type=to_struct_type(type); const struct_typet::componentst &components=struct_type.components(); result.operands().resize(components.size()); for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); it++) { result.operands()[it-components.begin()]= read_rhs(member_exprt(expr, it->get_name(), it->type()), loc); } return result; } // is this an object we track? if(ssa_objects.objects.find(object)!= ssa_objects.objects.end()) { return read_rhs(object, loc); } else { return name_input(object); } }
exprt dereference_rec( const exprt &src, const ssa_value_domaint &ssa_value_domain, const std::string &nondet_prefix, const namespacet &ns) { if(src.id()==ID_dereference) { const exprt &pointer=to_dereference_expr(src).pointer(); exprt pointer_deref=dereference(pointer, ssa_value_domain, nondet_prefix, ns); // We use the identifier produced by // local_SSAt::replace_side_effects_rec exprt result=symbol_exprt(nondet_prefix, src.type()); // query the value sets const ssa_value_domaint::valuest values= ssa_value_domain(pointer, ns); for(ssa_value_domaint::valuest::value_sett::const_iterator it=values.value_set.begin(); it!=values.value_set.end(); it++) { exprt guard=ssa_alias_guard(src, it->get_expr(), ns); exprt value=ssa_alias_value(src, it->get_expr(), ns); result=if_exprt(guard, value, result); } return result; } else if(src.id()==ID_member) { member_exprt tmp=to_member_expr(src); tmp.struct_op()=dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); #ifdef DEBUG std::cout << "dereference_rec tmp: " << from_expr(ns, "", tmp) << '\n'; #endif if(tmp.struct_op().is_nil()) return nil_exprt(); return lift_if(tmp); } else if(src.id()==ID_address_of) { address_of_exprt tmp=to_address_of_expr(src); tmp.object()=dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); if(tmp.object().is_nil()) return nil_exprt(); return lift_if(tmp); } else { exprt tmp=src; Forall_operands(it, tmp) *it=dereference_rec(*it, ssa_value_domain, nondet_prefix, ns); return tmp; } }
void remove_function_pointerst::remove_function_pointer( goto_programt &goto_program, goto_programt::targett target) { const code_function_callt &code= to_code_function_call(target->code); const exprt &function=code.function(); // this better have the right type code_typet call_type=to_code_type(function.type()); // refine the type in case the forward declaration was incomplete if(call_type.has_ellipsis() && call_type.parameters().empty()) { call_type.remove_ellipsis(); forall_expr(it, code.arguments()) call_type.parameters().push_back( code_typet::parametert(it->type())); } assert(function.id()==ID_dereference); assert(function.operands().size()==1); const exprt &pointer=function.op0(); // Is this simple? if(pointer.id()==ID_address_of && to_address_of_expr(pointer).object().id()==ID_symbol) { to_code_function_call(target->code).function()= to_address_of_expr(pointer).object(); return; } typedef std::list<exprt> functionst; functionst functions; bool return_value_used=code.lhs().is_not_nil(); // get all type-compatible functions // whose address is ever taken for(type_mapt::const_iterator f_it= type_map.begin(); f_it!=type_map.end(); f_it++) { // address taken? if(address_taken.find(f_it->first)==address_taken.end()) continue; // type-compatible? if(!is_type_compatible(return_value_used, call_type, f_it->second)) continue; if(f_it->first=="pthread_mutex_cleanup") continue; symbol_exprt expr; expr.type()=f_it->second; expr.set_identifier(f_it->first); functions.push_back(expr); } // the final target is a skip goto_programt final_skip; goto_programt::targett t_final=final_skip.add_instruction(); t_final->make_skip(); // build the calls and gotos goto_programt new_code_calls; goto_programt new_code_gotos; for(functionst::const_iterator it=functions.begin(); it!=functions.end(); it++) { // call function goto_programt::targett t1=new_code_calls.add_instruction(); t1->make_function_call(code); to_code_function_call(t1->code).function()=*it; // the signature of the function might not match precisely fix_argument_types(to_code_function_call(t1->code)); fix_return_type(to_code_function_call(t1->code), new_code_calls); // goto final goto_programt::targett t3=new_code_calls.add_instruction(); t3->make_goto(t_final, true_exprt()); // goto to call address_of_exprt address_of; address_of.object()=*it; address_of.type()=pointer_typet(); address_of.type().subtype()=it->type(); if(address_of.type()!=pointer.type()) address_of.make_typecast(pointer.type()); goto_programt::targett t4=new_code_gotos.add_instruction(); t4->make_goto(t1, equal_exprt(pointer, address_of)); } // fall-through if(add_safety_assertion) { goto_programt::targett t=new_code_gotos.add_instruction(); t->make_assertion(false_exprt()); t->source_location.set_property_class("pointer dereference"); t->source_location.set_comment("invalid function pointer"); } goto_programt new_code; // patch them all together new_code.destructive_append(new_code_gotos); new_code.destructive_append(new_code_calls); new_code.destructive_append(final_skip); // set locations Forall_goto_program_instructions(it, new_code) { irep_idt property_class=it->source_location.get_property_class(); irep_idt comment=it->source_location.get_comment(); it->source_location=target->source_location; it->function=target->function; if(!property_class.empty()) it->source_location.set_property_class(property_class); if(!comment.empty()) it->source_location.set_comment(comment); }
exprt address_canonizer( const exprt &address, const namespacet &ns) { assert(ns.follow(address.type()).id()==ID_pointer); if(address.id()==ID_address_of) { const address_of_exprt &address_of_expr= to_address_of_expr(address); const exprt &object=address_of_expr.object(); if(object.id()==ID_dereference) { // &*x ---> x return to_dereference_expr(object).pointer(); } else if(object.id()==ID_member) { // get offset exprt offset=member_offset_expr(to_member_expr(object), ns); // &x.m ---> (&x)+offset address_of_exprt address_of_expr(to_member_expr(object).struct_op()); exprt rec_result=address_canonizer(address_of_expr, ns); // rec. call pointer_typet byte_pointer(unsigned_char_type()); typecast_exprt typecast_expr(rec_result, byte_pointer); plus_exprt sum(typecast_expr, offset); if(sum.type()!=address.type()) sum.make_typecast(address.type()); return sum; } else if(object.id()==ID_index) { // &(x[i]) ---> (&x)+i address_of_exprt address_of_expr(to_index_expr(object).array()); exprt rec_result=address_canonizer(address_of_expr, ns); // rec. call pointer_typet pointer_type; pointer_type.subtype()=object.type(); typecast_exprt typecast_expr(rec_result, pointer_type); plus_exprt sum(typecast_expr, to_index_expr(object).index()); if(sum.type()!=address.type()) sum.make_typecast(address.type()); return sum; } else if(object.id()==ID_symbol && is_iterator(object)) { // address of iterator is dereferenced to a corresponding symbol - // will be bound to real address during analysis symbol_exprt iterator_addr( id2string(to_symbol_expr(object).get_identifier())+"'addr", address.type()); return iterator_addr; } else return address; } else if(address.id()==ID_plus || address.id()==ID_minus) { // one of the operands needs to be a pointer assert(address.operands().size()==2); exprt tmp=address; if(ns.follow(tmp.op0().type()).id()==ID_pointer) { tmp.op0()=address_canonizer(tmp.op0(), ns); return tmp; } else if(ns.follow(tmp.op1().type()).id()==ID_pointer) { tmp.op1()=address_canonizer(tmp.op1(), ns); return tmp; } else return tmp; } else if(address.id()==ID_if) { if_exprt tmp=to_if_expr(address); tmp.true_case()=address_canonizer(tmp.true_case(), ns); tmp.false_case()=address_canonizer(tmp.false_case(), ns); return tmp; } else if(address.id()==ID_typecast) { typecast_exprt tmp=to_typecast_expr(address); // cast from another pointer? if(tmp.op().type().id()==ID_pointer) { tmp.op()=address_canonizer(tmp.op(), ns); return tmp; } return address; } else return address; }
bool remove_const_function_pointerst::try_resolve_dereference( const dereference_exprt &deref_expr, expressionst &out_expressions, bool &out_is_const) { // We had a pointer, we need to check both the pointer // type can't be changed, and what it what pointing to // can't be changed expressionst pointer_values; bool pointer_const; bool resolved= try_resolve_expression(deref_expr.pointer(), pointer_values, pointer_const); if(resolved && pointer_const) { bool all_objects_const=true; for(const exprt &pointer_val : pointer_values) { if(pointer_val.id()==ID_address_of) { address_of_exprt address_expr=to_address_of_expr(pointer_val); bool object_const=false; expressionst out_object_values; bool resolved= try_resolve_expression( address_expr.object(), out_object_values, object_const); if(resolved) { out_expressions.insert( out_expressions.end(), out_object_values.begin(), out_object_values.end()); all_objects_const&=object_const; } else { LOG("Failed to resolve value of a dereference", address_expr); } } else { LOG( "Squashing dereference did not result in an address", pointer_val); return false; } } out_is_const=all_objects_const; return true; } else { if(!resolved) { LOG("Failed to resolve pointer of dereference", deref_expr); } else if(!pointer_const) { LOG("Pointer value not const so can't squash", deref_expr); } return false; } }
bool remove_const_function_pointerst::try_resolve_function_call( const exprt &expr, functionst &out_functions) { assert(out_functions.empty()); const exprt &simplified_expr=simplify_expr(expr, ns); bool resolved=false; functionst resolved_functions; if(simplified_expr.id()==ID_index) { const index_exprt &index_expr=to_index_expr(simplified_expr); resolved=try_resolve_index_of_function_call(index_expr, resolved_functions); } else if(simplified_expr.id()==ID_member) { const member_exprt &member_expr=to_member_expr(simplified_expr); resolved=try_resolve_member_function_call(member_expr, resolved_functions); } else if(simplified_expr.id()==ID_address_of) { address_of_exprt address_expr=to_address_of_expr(simplified_expr); resolved=try_resolve_address_of_function_call( address_expr, resolved_functions); } else if(simplified_expr.id()==ID_dereference) { const dereference_exprt &deref=to_dereference_expr(simplified_expr); resolved=try_resolve_dereference_function_call(deref, resolved_functions); } else if(simplified_expr.id()==ID_typecast) { typecast_exprt typecast_expr=to_typecast_expr(simplified_expr); resolved= try_resolve_typecast_function_call(typecast_expr, resolved_functions); } else if(simplified_expr.id()==ID_symbol) { if(simplified_expr.type().id()==ID_code) { resolved_functions.insert(simplified_expr); resolved=true; } else { LOG("Non const symbol wasn't squashed", simplified_expr); resolved=false; } } else { LOG("Unrecognised expression", simplified_expr); resolved=false; } if(resolved) { out_functions.insert(resolved_functions.begin(), resolved_functions.end()); return true; } else { return false; } }
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); } }
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; }