std::set<symbol_exprt> concurrency_aware_abstractort::targets_of_lvalue(const exprt& lvalue, goto_programt::const_targett program_location)
{

	std::set<symbol_exprt> result;
	if(lvalue.id() == ID_index)
	{
		exprt array_name = lvalue.op0();
		if(array_name.id() == ID_symbol)
		{
			result.insert(to_symbol_expr(array_name));
		} else {
			return targets_of_lvalue(array_name, program_location);
		}
	} else if(lvalue.id() == ID_member) {
		assert(lvalue.operands().size() == 1);
		return targets_of_lvalue(lvalue.op0(), program_location);
	} else if(lvalue.id() == ID_symbol) {
		result.insert(to_symbol_expr(lvalue));
	} else if(lvalue.id() == ID_dereference) {
		// We would like to add anything the pointer can point to,
		// but not the pointer itself

		value_setst::valuest value_set;
		pointer_info.get_values(program_location, lvalue.op0(), value_set);
		for(value_setst::valuest::iterator it = value_set.begin(); it != value_set.end(); it++)
		{
			if(it->id() != ID_object_descriptor)
			{
				// TODO: We may need to deal with this situation more carefully
				continue;
			}
			object_descriptor_exprt& object_descriptor = to_object_descriptor_expr(*it);
			if(object_descriptor.offset() != from_integer(0, index_type()))
			{
				std::cout << "Pointer " << from_expr(lvalue.op0()) << " can point to " << from_expr(*it) << " at line " <<
						program_location->location.get_line() << ", we cannot handle this" << std::endl;
				  exit(1);
			  }

			  if(object_descriptor.object().id() != ID_symbol)
			  {
				  std::cout << "Pointer " << from_expr(lvalue.op0()) << " can point to " << from_expr(*it) << " at line " <<
					  program_location->location.get_line() << ", we cannot handle this" << std::endl;
				  exit(1);
			  }

			  result.insert(to_symbol_expr(object_descriptor.object()));
		}

	} else {
		std::cout << "Cannot currently handle lvalue: " << from_expr(lvalue) << std::endl;
		assert(false);
	}
	return result;
}
std::set<symbol_exprt> locations_of_expression_rec(const predicatet& phi, const goto_programt::const_targett program_location, value_set_analysist& pointer_info, const namespacet& ns)
{
	if(phi.id()==ID_address_of)
	{
		// Addresses are constant -- don't need to read the symbol whose address we are taking
		return std::set<symbol_exprt>();
	}

	if(phi.id()==ID_constant)
	{
		// No symbols associated with a constant
		return std::set<symbol_exprt>();
	}

	if(phi.id()==ID_symbol)
	{
		std::set<symbol_exprt> result;
		result.insert(to_symbol_expr(phi));
		return result;
	}

	if(phi.id()==ID_dereference)
	{
		// We would like to add the pointer itself (which for now we require to be a variable)
		// and anything it can point to

		std::set<symbol_exprt> result;

		result = locations_of_expression_rec(phi.op0(), program_location, pointer_info, ns); // if we have *p, we insert the locations of p itself

		value_setst::valuest value_set;
		pointer_info.get_values(program_location, phi.op0(), value_set);

		for(value_setst::valuest::iterator it = value_set.begin(); it != value_set.end(); it++)
		{
			if(it->id() != ID_object_descriptor)
			{
				// TODO: We may need to deal with this situation more carefully
				continue;
			}

			object_descriptor_exprt& object_descriptor = to_object_descriptor_expr(*it);

			if(object_descriptor.object().id() == "NULL-object")
			{
				// No locations associated with NULL
				continue;
			}

			if(object_descriptor.offset() != from_integer(0, index_type()))
			{
				std::cout << "(*) Warning: pointer " << from_expr(ns, "", phi.op0()) << " can point to " << from_expr(ns, "", *it) << " at " <<
						program_location->location << ", this needs further investigation" << std::endl;
				continue;
			}

			if(object_descriptor.object().id() != ID_symbol)
			{
				std::cout << "(**) Warning: pointer " << from_expr(ns, "", phi.op0()) << " can point to " << from_expr(ns, "", *it) << " at " <<
						program_location->location << ", this needs further investigation" << std::endl;
				continue;
			}

			result.insert(to_symbol_expr(object_descriptor.object()));
		}

		return result;
	}

	if(phi.id()=="invalid-pointer" || phi.id()=="pointer_arithmetic" || phi.id()=="pointer_difference")
	{
		std::cout << "(***) Warning: cannot yet handle " << from_expr(ns, "", phi) << ", ignoring it" << std::endl;
		return std::set<symbol_exprt>();
	}


	{
		/* Default case: iterate through the operands of the expression, adding all locations referenced therein */
		std::set<symbol_exprt> result;
		for(unsigned int i = 0; i < phi.operands().size(); i++)
		{
			std::set<symbol_exprt> symbols_i = locations_of_expression_rec(phi.operands()[i], program_location, pointer_info, ns);
			for(std::set<symbol_exprt>::iterator it = symbols_i.begin(); it != symbols_i.end(); it++)
			{
				result.insert(*it);
			}
		}
		return result;
	}

}
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;
}