exprt get_component_rec(
  const exprt &struct_union,
  const irep_idt &component_name,
  const namespacet &ns)
{
  const struct_union_typet &struct_union_type=
    to_struct_union_type(ns.follow(struct_union.type()));

  const struct_union_typet::componentst &components=
    struct_union_type.components();

  for(struct_union_typet::componentst::const_iterator
      it=components.begin();
      it!=components.end();
      it++)
  {
    const typet &type=ns.follow(it->type());
  
    if(it->get_name()==component_name)
    {
      return make_member_expr(struct_union, *it, ns);
    }
    else if(it->get_anonymous() &&
            (type.id()==ID_struct || type.id()==ID_union))
    {
      exprt tmp=make_member_expr(struct_union, *it, ns);
      exprt result=get_component_rec(tmp, component_name, ns);
      if(result.is_not_nil()) return result;
    }
  }
  
  return nil_exprt();
}
exprt ssa_alias_guard(
  const exprt &e1,
  const exprt &e2,
  const namespacet &ns)
{
  exprt a1=address_canonizer(address_of_exprt(e1), ns);
  exprt a2=address_canonizer(address_of_exprt(e2), ns);
  
  // in some cases, we can use plain address equality,
  // as we assume well-aligned-ness
  mp_integer size1=pointer_offset_size(e1.type(), ns);
  mp_integer size2=pointer_offset_size(e2.type(), ns);
  
  if(size1>=size2)
  {
    exprt lhs=a1;
    exprt rhs=a2;
    if(ns.follow(rhs.type())!=ns.follow(lhs.type()))
      rhs=typecast_exprt(rhs, lhs.type());
  
    return equal_exprt(lhs, rhs);
  }
  
  return same_object(a1, a2);
}
Beispiel #3
0
exprt ssa_alias_guard(
  const exprt &e1,
  const exprt &e2,
  const namespacet &ns)
{
  exprt a1=address_canonizer(address_of_exprt(e1), ns);
  // TODO: We should compare 'base' pointers here because
  // we have a higher chance that there was no pointer arithmetic
  // on the base pointer than that the result of the pointer
  // arithmetic points to a base pointer.
  // The following hack does that:
  if(a1.id()==ID_plus)
    a1=a1.op0();

  exprt a2=address_canonizer(address_of_exprt(e2), ns);

  // in some cases, we can use plain address equality,
  // as we assume well-aligned-ness
  mp_integer size1=pointer_offset_size(e1.type(), ns);
  mp_integer size2=pointer_offset_size(e2.type(), ns);

  if(size1>=size2)
  {
    exprt lhs=a1;
    exprt rhs=a2;
    if(ns.follow(rhs.type())!=ns.follow(lhs.type()))
      rhs=typecast_exprt(rhs, lhs.type());

    return equal_exprt(lhs, rhs);
  }

  return same_object(a1, a2);
}
Beispiel #4
0
std::string array_name(
  const namespacet &ns,
  const exprt &expr)
{
  if(expr.id()==ID_index)
  {
    if(expr.operands().size()!=2)
      throw "index takes two operands";

    return array_name(ns, expr.op0())+"[]";
  }
  else if(is_ssa_expr(expr))
  {
    const symbolt &symbol=
      ns.lookup(to_ssa_expr(expr).get_object_name());
    return "array `"+id2string(symbol.base_name)+"'";
  }
  else if(expr.id()==ID_symbol)
  {
    const symbolt &symbol=ns.lookup(expr);
    return "array `"+id2string(symbol.base_name)+"'";
  }
  else if(expr.id()==ID_string_constant)
  {
    return "string constant";
  }
  else if(expr.id()==ID_member)
  {
    assert(expr.operands().size()==1);
    return array_name(ns, expr.op0())+"."+
           expr.get_string(ID_component_name);
  }

  return "array";
}
Beispiel #5
0
bool type_eq(const typet &type1, const typet &type2, const namespacet &ns)
{
  if(type1 == type2)
    return true;

  if(type1.id() == "symbol")
  {
    const symbolt &symbol = ns.lookup(type1);
    if(!symbol.is_type)
      throw "symbol " + id2string(symbol.name) + " is not a type";

    return type_eq(symbol.type, type2, ns);
  }

  if(type2.id() == "symbol")
  {
    const symbolt &symbol = ns.lookup(type2);
    if(!symbol.is_type)
      throw "symbol " + id2string(symbol.name) + " is not a type";

    return type_eq(type1, symbol.type, ns);
  }

  return false;
}
Beispiel #6
0
void show_symbol_table_plain(const namespacet &ns, std::ostream &out)
{
  out << std::endl << "Symbols:" << std::endl;
  out << "Number of symbols: " << ns.get_context().size() << std::endl;
  out << std::endl;

  ns.get_context().foreach_operand_in_order([&out, &ns](const symbolt &s) {
    int mode;

    if(s.mode == "")
      mode = 0;
    else
    {
      mode = get_mode(id2string(s.mode));
      if(mode < 0)
        throw "symbol " + id2string(s.name) + " has unknown mode";
    }

    std::unique_ptr<languaget> p(mode_table[mode].new_language());
    std::string type_str, value_str;

    if(s.type.is_not_nil())
      p->from_type(s.type, type_str, ns);

    if(s.value.is_not_nil())
      p->from_expr(s.value, value_str, ns);

    out << "Symbol......: " << s.name << std::endl;
    out << "Pretty name.: " << s.pretty_name << std::endl;
    out << "Module......: " << s.module << std::endl;
    out << "Base name...: " << s.base_name << std::endl;
    out << "Mode........: " << s.mode << " (" << mode << ")" << std::endl;
    out << "Type........: " << type_str << std::endl;
    out << "Value.......: " << value_str << std::endl;
    out << "Flags.......:";

    if(s.lvalue)
      out << " lvalue";
    if(s.static_lifetime)
      out << " static_lifetime";
    if(s.file_local)
      out << " file_local";
    if(s.is_type)
      out << " type";
    if(s.is_extern)
      out << " extern";
    if(s.is_macro)
      out << " macro";
    if(s.is_used)
      out << " used";

    out << std::endl;
    out << "Location....: " << s.location << std::endl;

    out << std::endl;
  });
}
Beispiel #7
0
/// automated variable renaming
/// \par parameters: symbol to be renamed, namespace
/// \return new symbol
void get_new_name(irep_idt &new_name, const namespacet &ns)
{
  const symbolt *symbol;
  if(ns.lookup(new_name, symbol))
    return;

  std::string prefix=id2string(new_name)+"_";

  new_name=prefix+std::to_string(ns.get_max(prefix)+1);
}
mp_integer compute_pointer_offset(
  const namespacet &ns,
  const exprt &expr)
{
  if(expr.id()==ID_symbol)
    return 0;
  else if(expr.id()==ID_index)
  {
    assert(expr.operands().size()==2);
    
    const typet &array_type=ns.follow(expr.op0().type());
    assert(array_type.id()==ID_array);

    mp_integer o=compute_pointer_offset(ns, expr.op0());
    
    if(o!=-1)
    {
      mp_integer sub_size=
        pointer_offset_size(ns, array_type.subtype());

      mp_integer i;

      if(sub_size!=0 && !to_integer(expr.op1(), i))
        return o+i*sub_size;
    }
      
    // don't know
  }
  else if(expr.id()==ID_member)
  {
    assert(expr.operands().size()==1);
    const typet &type=ns.follow(expr.op0().type());
    
    assert(type.id()==ID_struct ||
           type.id()==ID_union);

    mp_integer o=compute_pointer_offset(ns, expr.op0());

    if(o!=-1)
    {    
      if(type.id()==ID_union)
        return o;
    
      return o+member_offset(
        ns, to_struct_type(type), expr.get(ID_component_name));
    }
  }
  else if(expr.id()==ID_string_constant)
    return 0;

  return -1; // don't know
}
Beispiel #9
0
exprt get_failed_symbol(
  const symbol_exprt &expr,
  const namespacet &ns)
{
  const symbolt &symbol=ns.lookup(expr);
  irep_idt failed_symbol_id=symbol.type.get("#failed_symbol");

  if(failed_symbol_id==irep_idt())
    return nil_exprt();
    
  const symbolt &failed_symbol=ns.lookup(failed_symbol_id);
  
  return symbol_exprt(failed_symbol_id, failed_symbol.type);
}
Beispiel #10
0
void remove_function_pointerst::fix_return_type(
  code_function_callt &function_call,
  goto_programt &dest)
{  
  // are we returning anything at all?
  if(function_call.lhs().is_nil()) return;
  
  const code_typet &code_type=
    to_code_type(ns.follow(function_call.function().type()));
  
  // type already ok?
  if(type_eq(
       function_call.lhs().type(),
       code_type.return_type(), ns))
    return;

  symbolt &tmp_symbol=new_tmp_symbol();
  tmp_symbol.type=code_type.return_type();
  tmp_symbol.location=function_call.location();

  symbol_exprt tmp_symbol_expr;
  tmp_symbol_expr.type()=tmp_symbol.type;
  tmp_symbol_expr.set_identifier(tmp_symbol.name);
  
  exprt old_lhs=function_call.lhs();
  function_call.lhs()=tmp_symbol_expr;

  goto_programt::targett t_assign=dest.add_instruction();
  t_assign->make_assignment();
  t_assign->code=code_assignt(
    old_lhs, typecast_exprt(tmp_symbol_expr, old_lhs.type()));
}  
Beispiel #11
0
bool cpp_typecheck(
  exprt &expr,
  message_handlert &message_handler,
  const namespacet &ns)
{
  contextt context;
  cpp_parse_treet cpp_parse_tree;

  cpp_typecheckt cpp_typecheck(
    cpp_parse_tree, context, ns.get_context(), "", message_handler);

  try
  {
    cpp_typecheck.typecheck_expr(expr);
  }

  catch(int e)
  {
    cpp_typecheck.error();
  }

  catch(const char *e)
  {
    cpp_typecheck.error(e);
  }

  catch(const std::string &e)
  {
    cpp_typecheck.error(e);
  }

  return cpp_typecheck.get_error_found();
}
Beispiel #12
0
std::unique_ptr<languaget> get_language(
  const namespacet &ns,
  const irep_idt &identifier)
{
  if(identifier=="")
    return std::unique_ptr<languaget>(get_default_language());
  else
  {
    const symbolt *symbol;
    
    if(ns.lookup(identifier, symbol))
      return std::unique_ptr<languaget>(get_default_language());
    else if(symbol->mode=="")
      return std::unique_ptr<languaget>(get_default_language());
    else
    {
      languaget *ptr=get_language_from_mode(symbol->mode);

      if(ptr==NULL)
        throw "symbol `"+id2string(symbol->name)+
              "' has unknown mode '"+id2string(symbol->mode)+"'";

      return std::unique_ptr<languaget>(ptr);
    }
  }
}
bool has_component_rec(
  const typet &type,
  const irep_idt &component_name,
  const namespacet &ns)
{
  const struct_union_typet &struct_union_type=
    to_struct_union_type(ns.follow(type));

  const struct_union_typet::componentst &components=
    struct_union_type.components();

  for(struct_union_typet::componentst::const_iterator
      it=components.begin();
      it!=components.end();
      it++)
  {
    if(it->get_name()==component_name)
    {
      return true;
    }
    else if(it->get_anonymous())
    {
      if(has_component_rec(it->type(), component_name, ns))
        return true;
    }
  }
  
  return false;
}
Beispiel #14
0
bool ansi_c_typecheck(
  exprt &expr,
  message_handlert &message_handler,
  const namespacet &ns)
{
  symbol_tablet symbol_table;
  ansi_c_parse_treet ansi_c_parse_tree;

  ansi_c_typecheckt ansi_c_typecheck(
    ansi_c_parse_tree, symbol_table,
    ns.get_symbol_table(), "", message_handler);

  try
  {
    ansi_c_typecheck.typecheck_expr(expr);
  }

  catch(int)
  {
    ansi_c_typecheck.error();
  }

  catch(const char *e)
  {
    ansi_c_typecheck.error() << e << messaget::eom;
  }

  catch(const std::string &e)
  {
    ansi_c_typecheck.error() << e << messaget::eom;
  }

  return ansi_c_typecheck.get_error_found();
}
Beispiel #15
0
exprt remove_virtual_functionst::build_class_identifier(
  const exprt &src)
{
  // the class identifier is in the root class
  exprt e=src;
  
  while(1)
  {
    const typet &type=ns.follow(e.type());
    assert(type.id()==ID_struct);
    
    const struct_typet &struct_type=to_struct_type(type);
    const struct_typet::componentst &components=struct_type.components();
    assert(!components.empty());
    
    member_exprt member_expr(
      e, components.front().get_name(), components.front().type());
    
    if(components.front().get_name()=="@class_identifier")
    {
      // found it
      return member_expr;
    }
    else
    {
      e=member_expr;
    }
  }
}
void uninitialized_domaint::transform(
  locationt from,
  locationt to,
  ai_baset &ai,
  const namespacet &ns)
{
  if(has_values.is_false())
    return;

  switch(from->type)
  {
  case DECL:
    {
      const irep_idt &identifier=
        to_code_decl(from->code).get_identifier();
      const symbolt &symbol=ns.lookup(identifier);

      if(!symbol.is_static_lifetime)
        uninitialized.insert(identifier);
    }
    break;

  default:
    {
      std::list<exprt> read=expressions_read(*from);
      std::list<exprt> written=expressions_written(*from);

      forall_expr_list(it, written) assign(*it);

      // we only care about the *first* uninitalized use
      forall_expr_list(it, read) assign(*it);
    }
  }
}
Beispiel #17
0
std::string counterexample_value_binary(
  const exprt &expr,
  const namespacet &ns)
{
  const typet &type=ns.follow(expr.type());
  
  if(expr.id()==ID_constant)
  {
    if(type.id()==ID_unsignedbv ||
       type.id()==ID_signedbv ||
       type.id()==ID_bv ||
       type.id()==ID_fixedbv ||
       type.id()==ID_floatbv ||
       type.id()==ID_pointer)
    {
      return expr.get_string(ID_value);
    }
    else if(type.id()==ID_bool)
    {
      return expr.is_true()?"1":"0";
    }
  }
  else if(expr.id()==ID_array)
  {
    std::string result;
  
    forall_operands(it, expr)
    {
      if(result=="") result="{ "; else result+=", ";
      result+=counterexample_value_binary(*it, ns);
    }
      
    return result+" }";
  }
Beispiel #18
0
code_function_callt get_destructor(
  const namespacet &ns,
  const typet &type)
{
  if(type.id()==ID_symbol)
  {
    return get_destructor(ns, ns.follow(type));
  }
  else if(type.id()==ID_struct)
  {
    const struct_typet &struct_type=to_struct_type(type);

    const struct_typet::componentst &components=
      struct_type.components();

    for(struct_typet::componentst::const_iterator
        it=components.begin();
        it!=components.end();
        it++)
    {
      if(it->type().id()==ID_code)
      {
        const code_typet &code_type=to_code_type(it->type());
        
        if(code_type.return_type().id()==ID_destructor &&
           code_type.parameters().size()==1)
        {
          const typet &arg_type=code_type.parameters().front().type();
          
          if(arg_type.id()==ID_pointer &&
             ns.follow(arg_type.subtype())==type)
          {
            exprt symbol_expr(ID_symbol, it->type());
            symbol_expr.set(ID_identifier, it->get(ID_name));      

            code_function_callt function_call;
            function_call.function()=symbol_expr;
            
            return function_call;
          }
        }
      }
    }
  }

  return static_cast<const code_function_callt &>(get_nil_irep());
}
  void compute_address_taken_in_symbols(
    std::set<irep_idt> &address_taken)
  {
    const symbol_tablet &symbol_table=ns.get_symbol_table();

    for(const auto &s : symbol_table.symbols)
      compute_address_taken_functions(s.second.value, address_taken);
  }
Beispiel #20
0
void base_type_rec(
  typet &type, const namespacet &ns, std::set<irep_idt> &symb)
{
  if(type.id()==ID_symbol ||
     type.id()==ID_c_enum_tag ||
     type.id()==ID_struct_tag ||
     type.id()==ID_union_tag)
  {
    const symbolt *symbol;

    if(!ns.lookup(type.get(ID_identifier), symbol) &&
       symbol->is_type &&
       !symbol->type.is_nil())
    {
      type=symbol->type;
      base_type_rec(type, ns, symb); // recursive call
      return;
    }
  }
  else if(type.id()==ID_array)
  {
    base_type_rec(to_array_type(type).subtype(), ns, symb);
  }
  else if(type.id()==ID_struct ||
          type.id()==ID_union)
  {
    struct_union_typet::componentst &components=
      to_struct_union_type(type).components();

    for(auto &component : components)
      base_type_rec(component.type(), ns, symb);
  }
  else if(type.id()==ID_pointer)
  {
    typet &subtype=to_pointer_type(type).subtype();

    // we need to avoid running into an infinite loop
    if(subtype.id()==ID_symbol ||
       subtype.id()==ID_c_enum_tag ||
       subtype.id()==ID_struct_tag ||
       subtype.id()==ID_union_tag)
    {
      const irep_idt &id=subtype.get(ID_identifier);

      if(symb.find(id)!=symb.end())
        return;

      symb.insert(id);

      base_type_rec(subtype, ns, symb);

      symb.erase(id);
    }
    else
      base_type_rec(subtype, ns, symb);
  }
}
Beispiel #21
0
unsigned safe_width(const exprt &e, const namespacet &ns)
{
  const typet &type=ns.follow(e.type());

  if(type.id()=="pointer" || type.id()=="reference")
    return config.ansi_c.pointer_width;
  
  return boolbv_widtht(ns)(type);
}
Beispiel #22
0
void constant_propagator_domaint::assign_rec(
  valuest &values,
  const exprt &lhs, const exprt &rhs,
  const namespacet &ns)
{
  const typet & lhs_type = ns.follow(lhs.type());
  const typet & rhs_type = ns.follow(rhs.type());

#ifdef DEBUG
  std::cout << "assign: " << from_expr(ns, "", lhs)
            << " := " << from_type(ns, "", rhs_type) << '\n';
#endif

  if(lhs.id()==ID_symbol && rhs.id()==ID_if)
  {
	exprt cond=rhs.op0();
	assert(cond.operands().size()==2);
	if(values.is_constant(cond.op0())
			&& values.is_constant(cond.op1()))
	{
      if(cond.op0().id()==ID_index)
      {
    	exprt index=cond.op0();
        exprt new_expr=concatenate_array_id(index.op0(), index.op1(), index.type());
        values.replace_const(new_expr);
        cond.op0()=new_expr;
        cond = simplify_expr(cond,ns);
      }
      else
        assert(0);

      assign(values, to_symbol_expr(lhs), cond, ns);
	}
  }
  else if(lhs.id()==ID_symbol && rhs_type.id()!=ID_array
                         && rhs_type.id()!=ID_struct
                         && rhs_type.id()!=ID_union)
  {
    if(values.is_constant(rhs))
      assign(values, to_symbol_expr(lhs), rhs, ns);
    else
      values.set_to_top(to_symbol_expr(lhs));
  }
  else if(lhs.id()==ID_symbol && lhs_type.id()==ID_array
		                       && rhs_type.id()==ID_array)
  {
	exprt new_expr;
	mp_integer idx=0;
    forall_operands(it, rhs)
	{
  	  new_expr=concatenate_array_id(lhs, idx, it->type());
  	  assign(values, to_symbol_expr(new_expr), *it, ns);
  	  idx = idx +1;
	}
  void compute_address_taken_in_symbols(
    std::set<irep_idt> &address_taken)
  {
    const symbol_tablet &symbol_table=ns.get_symbol_table();

    const symbol_tablet::symbolst &s=symbol_table.symbols;

    for(symbol_tablet::symbolst::const_iterator
        it=s.begin(); it!=s.end(); ++it)
      compute_address_taken_functions(it->second.value, address_taken);
  }
bool ansi_c_typecheck(
  exprt &expr,
  message_handlert &message_handler,
  const namespacet &ns)
{
  contextt context1, context2;
  ansi_c_parse_treet ansi_c_parse_tree;

  context1 = ns.get_context();

#if 0
  ansi_c_typecheckt ansi_c_typecheck(
    ansi_c_parse_tree, context,
    ns.get_context(), "", message_handler);
#endif
  ansi_c_typecheckt ansi_c_typecheck(
    ansi_c_parse_tree, context1,
    context2, "", message_handler);

  try
  {
    ansi_c_typecheck.typecheck_expr(expr);
  }

  catch(int e)
  {
    ansi_c_typecheck.error();
  }

  catch(const char *e)
  {
    ansi_c_typecheck.error(e);
  }

  catch(const std::string &e)
  {
    ansi_c_typecheck.error(e);
  }
  
  return ansi_c_typecheck.get_error_found();
}
Beispiel #25
0
exprt ssa_alias_value(
  const exprt &e1,
  const exprt &e2,
  const namespacet &ns)
{
  const typet &e1_type=ns.follow(e1.type());
  const typet &e2_type=ns.follow(e2.type());

  // type matches?
  if(e1_type==e2_type)
    return e2;

  exprt a1=address_canonizer(address_of_exprt(e1), ns);
  exprt a2=address_canonizer(address_of_exprt(e2), ns);

  exprt offset1=pointer_offset(a1);

  // array index possible?
  if(e2_type.id()==ID_array &&
     e1_type==ns.follow(e2_type.subtype()))
  {
    // this assumes well-alignedness

    mp_integer element_size=pointer_offset_size(e2_type.subtype(), ns);

    if(element_size==1)
      return index_exprt(e2, offset1, e1.type());
    else if(element_size>1)
    {
      exprt index=
        div_exprt(offset1, from_integer(element_size, offset1.type()));
      return index_exprt(e2, index, e1.type());
    }
  }

  byte_extract_exprt byte_extract(byte_extract_id(), e1.type());
  byte_extract.op()=e2;
  byte_extract.offset()=offset1;

  return byte_extract;
}
Beispiel #26
0
code_function_callt get_destructor(
  const namespacet &ns,
  const typet &type)
{
  if(type.id()==ID_symbol)
  {
    return get_destructor(ns, ns.follow(type));
  }
  else if(type.id()==ID_struct)
  {
    const exprt &methods=static_cast<const exprt&>(type.find(ID_methods));

    forall_operands(it, methods)
    {
      if(it->type().id()==ID_code)
      {
        const code_typet &code_type=to_code_type(it->type());

        if(code_type.return_type().id()==ID_destructor &&
           code_type.parameters().size()==1)
        {
          const typet &arg_type=code_type.parameters().front().type();

          if(arg_type.id()==ID_pointer &&
             ns.follow(arg_type.subtype())==type)
          {
            exprt symbol_expr(ID_symbol, it->type());
            symbol_expr.set(ID_identifier, it->get(ID_name));

            code_function_callt function_call;
            function_call.function()=symbol_expr;

            return function_call;
          }
        }
      }
    }
  }

  return static_cast<const code_function_callt &>(get_nil_irep());
}
void get_symbols_rec(
  const namespacet &ns,
  const symbolt &symbol,
  find_symbols_sett &dest)
{
  dest.insert(symbol.name);
  
  find_symbols_sett new_symbols;

  find_type_and_expr_symbols(symbol.type, new_symbols);
  find_type_and_expr_symbols(symbol.value, new_symbols);
  
  if(symbol.type.id()==ID_code)
  {
    const code_typet &code_type=to_code_type(symbol.type);
    const code_typet::parameterst &parameters=code_type.parameters();

    for(code_typet::parameterst::const_iterator
        it=parameters.begin();
        it!=parameters.end();
        it++)
    {
      irep_idt id=it->get_identifier();
      const symbolt *s;
      // identifiers for prototypes need not exist
      if(!ns.lookup(id, s)) new_symbols.insert(id);
    }
  }

  for(find_symbols_sett::const_iterator
      it=new_symbols.begin();
      it!=new_symbols.end();
      it++)
  {
    if(dest.find(*it)==dest.end())
    {
      dest.insert(*it);
      get_symbols_rec(ns, ns.lookup(*it), dest); // recursive call
    }
  }
}
Beispiel #28
0
inline static unsigned bv_width(
  const typet &type,
  const namespacet &ns)
{
  if(type.id()==ID_c_enum_tag)
  {
    const typet &t=ns.follow_tag(to_c_enum_tag_type(type));
    assert(t.id()==ID_c_enum);
    return bv_width(t.subtype(), ns);
  }

  return unsafe_string2unsigned(type.get_string(ID_width));
}
exprt remove_virtual_functionst::get_method(
  const irep_idt &class_id,
  const irep_idt &component_name) const
{
  irep_idt id=id2string(class_id)+"."+
              id2string(component_name);

  const symbolt *symbol;
  if(ns.lookup(id, symbol))
    return nil_exprt();

  return symbol->symbol_expr();
}
Beispiel #30
0
bool value_sett::field_sensitive(
  const irep_idt &id,
  const typet &type,
  const namespacet &ns)
{
  // we always track fields on these
  if(has_prefix(id2string(id), "value_set::dynamic_object") ||
     id=="value_set::return_value" ||
     id=="value_set::memory")
    return true;

  // otherwise it has to be a struct
  return ns.follow(type).id()==ID_struct;
}