Пример #1
0
void cpp_typecheckt::convert(cpp_declarationt &declaration)
{
  // see if the declaration is empty
  if(declaration.find(ID_type).is_nil() &&
     !declaration.has_operands())
    return;

  // Record the function bodies so we can check them later.
  // This function is used recursively, so we save them.
  function_bodiest old_function_bodies=function_bodies;
  function_bodies.clear();

  // templates are done in a dedicated function
  if(declaration.is_template())
    convert_template_declaration(declaration);
  else
    convert_non_template_declaration(declaration);

  typecheck_function_bodies();
  function_bodies=old_function_bodies;
}
Пример #2
0
void cpp_typecheckt::convert_anonymous_union(
  cpp_declarationt &declaration,
  codet &code)
{
  codet new_code(ID_decl_block);
  new_code.reserve_operands(declaration.declarators().size());

  // unnamed object
  std::string identifier="#anon_union"+i2string(anon_counter++);

  irept name(ID_name);
  name.set(ID_identifier, identifier);
  name.set(ID_C_source_location, declaration.source_location());

  cpp_namet cpp_name;
  cpp_name.move_to_sub(name);
  cpp_declaratort declarator;
  declarator.name()=cpp_name;

  cpp_declarator_convertert cpp_declarator_converter(*this);

  const symbolt &symbol=
    cpp_declarator_converter.convert(declaration, declarator);

  if(!cpp_is_pod(declaration.type()))
  {
   error().source_location=follow(declaration.type()).source_location();
   error() << "anonymous union is not POD" << eom;
   throw 0;
  }

  codet decl_statement(ID_decl);
  decl_statement.reserve_operands(2);
  decl_statement.copy_to_operands(cpp_symbol_expr(symbol));

  new_code.move_to_operands(decl_statement);

  // do scoping
  symbolt union_symbol=symbol_table.symbols[follow(symbol.type).get(ID_name)];
  const irept::subt &components=union_symbol.type.add(ID_components).get_sub();

  forall_irep(it, components)
  {
    if(it->find(ID_type).id()==ID_code)
    {
      error().source_location=union_symbol.type.source_location();
      error() << "anonymous union `" << union_symbol.base_name
              << "' shall not have function members" << eom;
      throw 0;
    }

    const irep_idt &base_name=it->get(ID_base_name);

    if(cpp_scopes.current_scope().contains(base_name))
    {
      error().source_location=union_symbol.type.source_location();
      error() << "identifier `" << base_name << "' already in scope"
              << eom;
      throw 0;
    }

    cpp_idt &id=cpp_scopes.current_scope().insert(base_name);
    id.id_class = cpp_idt::SYMBOL;
    id.identifier=it->get(ID_name);
    id.class_identifier=union_symbol.name;
    id.is_member=true;
  }

  symbol_table.symbols[union_symbol.name].type.set(
    "#unnamed_object", symbol.base_name);

  code.swap(new_code);
}
Пример #3
0
void cpp_typecheckt::convert_non_template_declaration(
  cpp_declarationt &declaration)
{
  assert(!declaration.is_template());

  // we first check if this is a typedef
  typet &declaration_type=declaration.type();
  bool is_typedef=declaration.is_typedef();

  declaration.name_anon_struct_union();
  typecheck_type(declaration_type);
  
  // Elaborate any class template instance _unless_ we do a typedef.
  // These are only elaborated on usage!
  if(!is_typedef)
    elaborate_class_template(declaration_type);
  
  // Special treatment for anonymous unions
  if(declaration.declarators().empty() &&
     follow(declaration.type()).get_bool(ID_C_is_anonymous))
  {
    typet final_type=follow(declaration.type());

    if(final_type.id()!=ID_union)
    {
      error().source_location=final_type.source_location();
      error() << "top-level declaration does not declare anything"
              << eom;
      throw 0;
    }

    codet dummy;
    convert_anonymous_union(declaration, dummy);
  }

  // do the declarators (optional)
  Forall_cpp_declarators(it, declaration)
  {
    // copy the declarator (we destroy the original)
    cpp_declaratort declarator=*it;

    cpp_declarator_convertert cpp_declarator_converter(*this);

    cpp_declarator_converter.is_typedef=is_typedef;

    symbolt &symbol=cpp_declarator_converter.convert(
      declaration_type, declaration.storage_spec(),
      declaration.member_spec(), declarator);

    // any template instance to remember?
    if(declaration.find(ID_C_template).is_not_nil())
    {
      symbol.type.set(ID_C_template, declaration.find(ID_C_template));
      symbol.type.set(ID_C_template_arguments, declaration.find(ID_C_template_arguments));
    }

    // replace declarator by symbol expression
    exprt tmp=cpp_symbol_expr(symbol);
    it->swap(tmp);

    // is there a constructor to be called for the declarator?
    if(symbol.is_lvalue &&
       declarator.init_args().has_operands())
    {
      symbol.value=
        cpp_constructor(
          symbol.location,
          cpp_symbol_expr(symbol),
          declarator.init_args().operands());
    }
  }
Пример #4
0
void cpp_typecheckt::typecheck_class_template(
  cpp_declarationt &declaration)
{
  // Do template parameters. This also sets up the template scope.
  cpp_scopet &template_scope=
    typecheck_template_parameters(declaration.template_type());

  typet &type=declaration.type();
  template_typet &template_type=declaration.template_type();

  bool has_body=type.find(ID_body).is_not_nil();

  const cpp_namet &cpp_name=
    static_cast<const cpp_namet &>(type.find(ID_tag));

  if(cpp_name.is_nil())
  {
    err_location(type.source_location());
    throw "class templates must not be anonymous";
  }

  if(!cpp_name.is_simple_name())
  {
    err_location(cpp_name.source_location());
    throw "simple name expected as class template tag";
  }

  irep_idt base_name=cpp_name.get_base_name();

  const cpp_template_args_non_tct &partial_specialization_args=
    declaration.partial_specialization_args();
  
  const irep_idt symbol_name=
    class_template_identifier(
      base_name, template_type, partial_specialization_args);

  #if 0
  // Check if the name is already used by a different template
  // in the same scope.
  {
    cpp_scopet::id_sett id_set;
    cpp_scopes.current_scope().lookup(
      base_name,
      cpp_scopet::SCOPE_ONLY,
      cpp_scopet::TEMPLATE,
      id_set);

    if(!id_set.empty())
    {
      const symbolt &previous=lookup((*id_set.begin())->identifier);
      if(previous.name!=symbol_name || id_set.size()>1)
      {
        err_location(cpp_name.source_location());
        str << "template declaration of `" << base_name.c_str()
            << " does not match previous declaration\n";
        str << "location of previous definition: " << previous.location;
        throw 0;
      }
    }
  }
  #endif

  // check if we have it already
  
  symbol_tablet::symbolst::iterator previous_symbol=
    symbol_table.symbols.find(symbol_name);

  if(previous_symbol!=symbol_table.symbols.end())
  {
    // there already
    cpp_declarationt &previous_declaration=
      to_cpp_declaration(previous_symbol->second.type);
  
    bool previous_has_body=
      previous_declaration.type().find(ID_body).is_not_nil();

    // check if we have 2 bodies
    if(has_body && previous_has_body)
    {
      err_location(cpp_name.source_location());
      str << "template struct `" << base_name
          << "' defined previously" << std::endl;
      str << "location of previous definition: "
          << previous_symbol->second.location;
      throw 0;
    }
    
    if(has_body)
    {
      // We replace the template!
      // We have to retain any default parameters from the previous declaration.
      salvage_default_arguments(
        previous_declaration.template_type(),
        declaration.template_type());
      
      previous_symbol->second.type.swap(declaration);
      
      #if 0
      std::cout << "*****\n";
      std::cout << *cpp_scopes.id_map[symbol_name];
      std::cout << "*****\n";
      std::cout << "II: " << symbol_name << std::endl;
      #endif

      // We also replace the template scope (the old one could be deleted).
      cpp_scopes.id_map[symbol_name]=&template_scope;
      
      // We also fix the parent scope in order to see the new
      // template arguments
    }
    else
    {
      // just update any default arguments
      salvage_default_arguments(
        declaration.template_type(),
        previous_declaration.template_type());
    }
    
    assert(cpp_scopes.id_map[symbol_name]->id_class == cpp_idt::TEMPLATE_SCOPE);
    return;
  }

  // it's not there yet

  symbolt symbol;

  symbol.name=symbol_name;
  symbol.base_name=base_name;
  symbol.location=cpp_name.source_location();
  symbol.mode=ID_cpp;
  symbol.module=module;
  symbol.type.swap(declaration);
  symbol.is_macro=false;
  symbol.value=exprt("template_decls");

  symbol.pretty_name=
    cpp_scopes.current_scope().prefix+id2string(symbol.base_name);

  symbolt *new_symbol;
  if(symbol_table.move(symbol, new_symbol))
    throw "cpp_typecheckt::typecheck_compound_type: symbol_table.move() failed";

  // put into current scope
  cpp_idt &id=cpp_scopes.put_into_scope(*new_symbol);
  id.id_class=cpp_idt::TEMPLATE;
  id.prefix=cpp_scopes.current_scope().prefix+
            id2string(new_symbol->base_name);

  // link the template symbol with the template scope
  cpp_scopes.id_map[symbol_name]=&template_scope;
  assert(cpp_scopes.id_map[symbol_name]->id_class==cpp_idt::TEMPLATE_SCOPE);
}
Пример #5
0
void cpp_typecheckt::convert_class_template_specialization(
  cpp_declarationt &declaration)
{
  cpp_save_scopet saved_scope(cpp_scopes);

  typet &type=declaration.type();

  assert(type.id()==ID_struct);

  cpp_namet &cpp_name=
    static_cast<cpp_namet &>(type.add(ID_tag));

  if(cpp_name.is_qualified())
  {
    err_location(cpp_name.source_location());
    str << "qualifiers not expected here";
    throw 0;
  }
  
  if(cpp_name.get_sub().size()!=2 ||
     cpp_name.get_sub()[0].id()!=ID_name ||
     cpp_name.get_sub()[1].id()!=ID_template_args)
  {
    // currently we are more restrictive
    // than the standard
    err_location(cpp_name.source_location());
    str << "bad template-class-sepcialization name";                                                
    throw 0;
  }

  irep_idt base_name=
    cpp_name.get_sub()[0].get(ID_identifier);

  // copy the template arguments    
  const cpp_template_args_non_tct template_args_non_tc=
    to_cpp_template_args_non_tc(cpp_name.get_sub()[1]);

  // Remove the template arguments from the name.
  cpp_name.get_sub().pop_back();

  // get the template symbol

  cpp_scopest::id_sett id_set;
  cpp_scopes.current_scope().lookup(
    base_name, cpp_scopet::SCOPE_ONLY, cpp_idt::TEMPLATE, id_set);

  // remove any specializations
  for(cpp_scopest::id_sett::iterator
      it=id_set.begin();
      it!=id_set.end();
      ) // no it++
  {
    cpp_scopest::id_sett::iterator next=it;
    next++;
    
    if(lookup((*it)->identifier).type.
         find("specialization_of").is_not_nil())
      id_set.erase(it);
    
    it=next;
  }

  // only one should be left
  if(id_set.empty())
  {
    err_location(type.source_location());
    str << "class template `" << base_name << "' not found";
    throw 0;
  }
  else if(id_set.size()>1)
  {
    err_location(type);
    str << "class template `" << base_name << "' is ambiguous";
    throw 0;
  }
  
  symbol_tablet::symbolst::iterator s_it=
    symbol_table.symbols.find((*id_set.begin())->identifier);
    
  assert(s_it!=symbol_table.symbols.end());
  
  symbolt &template_symbol=s_it->second;
    
  if(!template_symbol.type.get_bool(ID_is_template))
  {
    err_location(type);
    str << "expected a template";
  }

  #if 0
  // is this partial specialization?  
  if(declaration.template_type().parameters().empty())
  {
    // typecheck arguments -- these are for the 'primary' template!
    cpp_template_args_tct template_args_tc=
      typecheck_template_args(
        declaration.source_location(),
        to_cpp_declaration(template_symbol.type).template_type(),
        template_args_non_tc);
    
    // Full specialization, i.e., template<>.
    // We instantiate.
    instantiate_template(
      cpp_name.source_location(),
      template_symbol,
      template_args_tc,
      type);
  }
  else
  #endif
  
  {
    // partial specialization -- we typecheck
    declaration.partial_specialization_args()=template_args_non_tc;
    declaration.set_specialization_of(template_symbol.name);

    typecheck_class_template(declaration);
  }
}
Пример #6
0
void cpp_typecheckt::convert_template_function_or_member_specialization(
  cpp_declarationt &declaration)
{
  cpp_save_scopet saved_scope(cpp_scopes);
  
  if(declaration.declarators().size()!=1 ||
     declaration.declarators().front().type().id()!=ID_function_type)
  {
    err_location(declaration.type());
    str << "expected function template specialization";
    throw 0;
  }

  assert(declaration.declarators().size()==1);
  cpp_declaratort declarator=declaration.declarators().front();
  cpp_namet &cpp_name=declarator.name();

  if(cpp_name.is_qualified())
  {
    err_location(cpp_name.source_location());
    str << "qualifiers not expected here";
    throw 0;
  }
  
  // There is specialization (instantiation with template arguments)
  // but also function overloading (no template arguments)
  
  assert(!cpp_name.get_sub().empty());
  
  if(cpp_name.get_sub().back().id()==ID_template_args)
  {
    // proper specialization with arguments
    if(cpp_name.get_sub().size()!=2 ||
       cpp_name.get_sub()[0].id()!=ID_name ||
       cpp_name.get_sub()[1].id()!=ID_template_args)
    {
      // currently we are more restrictive
      // than the standard
      err_location(cpp_name.source_location());
      str << "bad template-function-specialization name";
      throw 0;
    }

    std::string base_name=
      cpp_name.get_sub()[0].get(ID_identifier).c_str();

    cpp_scopest::id_sett id_set;
    cpp_scopes.current_scope().lookup(
      base_name, cpp_scopet::SCOPE_ONLY, id_set);

    if(id_set.empty())
    {
      err_location(cpp_name.source_location());
      str << "template function `" << base_name << "' not found";
      throw 0;
    }
    else if(id_set.size()>1)
    {
      err_location(cpp_name.source_location());
      str << "template function `" << base_name << "' is ambiguous";
    }
    
    const symbolt &template_symbol=
      lookup((*id_set.begin())->identifier);

    cpp_template_args_tct template_args=
      typecheck_template_args(
        declaration.source_location(),
        template_symbol,
        to_cpp_template_args_non_tc(cpp_name.get_sub()[1]));

    cpp_name.get_sub().pop_back();

    typet specialization;
    specialization.swap(declarator);

    instantiate_template(
      cpp_name.source_location(),
      template_symbol,
      template_args,
      template_args,
      specialization);
  }
  else
  {
    // Just overloading, but this is still a template
    // for disambiguation purposes!
    // http://www.gotw.ca/publications/mill17.htm
    cpp_declarationt new_declaration=declaration;
    
    new_declaration.remove(ID_template_type);
    new_declaration.remove(ID_is_template);
    new_declaration.set(ID_C_template, ""); // todo, get identifier
    
    convert_non_template_declaration(new_declaration);
  }
}
Пример #7
0
void cpp_typecheckt::typecheck_class_template_member(
  cpp_declarationt &declaration)
{
  assert(declaration.declarators().size()==1);

  cpp_declaratort &declarator=declaration.declarators()[0];
  const cpp_namet &cpp_name=to_cpp_name(declarator.add(ID_name));

  assert(cpp_name.is_qualified() ||
         cpp_name.has_template_args());

  // must be of the form: name1<template_args>::name2
  // or:                  name1<template_args>::operator X
  if(cpp_name.get_sub().size()==4 &&
     cpp_name.get_sub()[0].id()==ID_name &&
     cpp_name.get_sub()[1].id()==ID_template_args &&
     cpp_name.get_sub()[2].id()=="::" &&
     cpp_name.get_sub()[3].id()==ID_name)
  {
  }
  else if(cpp_name.get_sub().size()==5 &&
          cpp_name.get_sub()[0].id()==ID_name &&
          cpp_name.get_sub()[1].id()==ID_template_args &&
          cpp_name.get_sub()[2].id()=="::" &&
          cpp_name.get_sub()[3].id()==ID_operator)
  {
  }
  else
  {
    return; // TODO
    err_location(cpp_name);
    str << "bad template name";
    throw 0;
  }

  // let's find the class template this function template belongs to.
  cpp_scopet::id_sett id_set;

  cpp_scopes.current_scope().lookup(
    cpp_name.get_sub().front().get(ID_identifier),
    cpp_scopet::SCOPE_ONLY, // look only in current scope
    cpp_scopet::TEMPLATE,   // must be template
    id_set);

  if(id_set.empty())
  {
    str << cpp_scopes.current_scope();
    err_location(cpp_name);
    str << "class template `"
        << cpp_name.get_sub().front().get(ID_identifier)
        << "' not found";
    throw 0;
  }
  else if(id_set.size()>1)
  {
    err_location(cpp_name);
    str << "class template `"
        << cpp_name.get_sub().front().get(ID_identifier)
        << "' is ambiguous";
    throw 0;
  }
  else if((*(id_set.begin()))->id_class!=cpp_idt::TEMPLATE)
  {
    // std::cerr << *(*id_set.begin()) << std::endl;
    err_location(cpp_name);
    str << "class template `"
        << cpp_name.get_sub().front().get(ID_identifier)
        << "' is not a template";
    throw 0;
  }

  const cpp_idt &cpp_id=**(id_set.begin());
  symbolt &template_symbol=
    symbol_table.symbols.find(cpp_id.identifier)->second;

  exprt &template_methods=static_cast<exprt &>(
    template_symbol.value.add("template_methods"));
    
  template_methods.copy_to_operands(declaration);

  // save current scope
  cpp_save_scopet cpp_saved_scope(cpp_scopes);

  const irept &instantiated_with = 
    template_symbol.value.add("instantiated_with");
    
  for(unsigned i=0; i<instantiated_with.get_sub().size(); i++)
  {
    const cpp_template_args_tct &tc_template_args=
      static_cast<const cpp_template_args_tct &>(instantiated_with.get_sub()[i]);

    cpp_declarationt decl_tmp=declaration;

    // do template arguments
    // this also sets up the template scope of the method
    cpp_scopet &method_scope=
      typecheck_template_parameters(decl_tmp.template_type());

    cpp_scopes.go_to(method_scope);

    // mapping from template arguments to values/types
    template_map.build(decl_tmp.template_type(), tc_template_args);

    decl_tmp.remove(ID_template_type);
    decl_tmp.remove(ID_is_template);

    convert(decl_tmp);
    cpp_saved_scope.restore();
  }
}
Пример #8
0
void cpp_typecheckt::typecheck_function_template(
  cpp_declarationt &declaration)
{
  assert(declaration.declarators().size()==1);

  cpp_declaratort &declarator=declaration.declarators()[0];
  const cpp_namet &cpp_name=to_cpp_name(declarator.add(ID_name));

  // do template arguments
  // this also sets up the template scope
  cpp_scopet &template_scope=
    typecheck_template_parameters(declaration.template_type());

  if(!cpp_name.is_simple_name())
  {
    err_location(declaration);
    str << "function template must have simple name";
    throw 0;
  }

  irep_idt base_name=cpp_name.get_base_name();

  template_typet &template_type=declaration.template_type();
    
  typet function_type=
    declarator.merge_type(declaration.type());

  cpp_convert_plain_type(function_type);

  irep_idt symbol_name=
    function_template_identifier(
      base_name,
      template_type,
      function_type);

  bool has_value=declarator.find(ID_value).is_not_nil();

  // check if we have it already

  symbol_tablet::symbolst::iterator previous_symbol=
    symbol_table.symbols.find(symbol_name);

  if(previous_symbol!=symbol_table.symbols.end())
  {
    bool previous_has_value =
     to_cpp_declaration(previous_symbol->second.type).
       declarators()[0].find(ID_value).is_not_nil();

    if(has_value && previous_has_value)
    {
      err_location(cpp_name.source_location());
      str << "function template symbol `" << base_name
          << "' declared previously" << std::endl;
      str << "location of previous definition: "
          << previous_symbol->second.location;
      throw 0;
    }

    if(has_value)
    {
      previous_symbol->second.type.swap(declaration);
      cpp_scopes.id_map[symbol_name]=&template_scope;
    }

    // todo: the old template scope now is useless,
    // and thus, we could delete it
    return;
  }

  symbolt symbol;
  symbol.name=symbol_name;
  symbol.base_name=base_name;
  symbol.location=cpp_name.source_location();
  symbol.mode=ID_cpp;
  symbol.module=module;
  symbol.value.make_nil();

  symbol.type.swap(declaration);
  symbol.pretty_name=
    cpp_scopes.current_scope().prefix+id2string(symbol.base_name);

  symbolt *new_symbol;
  if(symbol_table.move(symbol, new_symbol))
    throw "cpp_typecheckt::typecheck_compound_type: symbol_table.move() failed";

  // put into scope
  cpp_idt &id=cpp_scopes.put_into_scope(*new_symbol);
  id.id_class=cpp_idt::TEMPLATE;
  id.prefix=cpp_scopes.current_scope().prefix+
            id2string(new_symbol->base_name);

  // link the template symbol with the template scope
  assert(template_scope.id_class==cpp_idt::TEMPLATE_SCOPE);
  cpp_scopes.id_map[symbol_name] = &template_scope;
}
Пример #9
0
void cpp_typecheckt::convert_template_declaration(
  cpp_declarationt &declaration)
{
  assert(declaration.is_template());

  if(declaration.member_spec().is_virtual())
  {
    err_location(declaration);
    str <<  "invalid use of 'virtual' in template declaration";
    throw 0;
  }

  if(declaration.is_typedef())
  {
    err_location(declaration);
    str << "template declaration for typedef";
    throw 0;
  }

  typet &type=declaration.type();

  // there are
  // 1) function templates
  // 2) class templates
  // 3) template members of class templates (static or methods)
  // 4) variable templates (C++14)

  if(declaration.is_class_template())
  {
    // there should not be declarators
    if(!declaration.declarators().empty())
    {
      err_location(declaration);
      throw "class template not expected to have declarators";
    }

    // it needs to be a class template
    if(type.id()!=ID_struct)
    {
      err_location(declaration);
      throw "expected class template";
    }

    // Is it class template specialization?
    // We can tell if there are template arguments in the class name,
    // like template<...> class tag<stuff> ...
    if((static_cast<const cpp_namet &>(
       type.find(ID_tag))).has_template_args())
    {
      convert_class_template_specialization(declaration);
      return;
    }

    typecheck_class_template(declaration);
    return;
  }
  else // maybe function template, maybe class template member, maye template variable
  {
    // there should be declarators in either case
    if(declaration.declarators().empty())
    {
      err_location(declaration);
      throw "non-class template is expected to have a declarator";
    }

    // Is it function template specialization?
    // Only full specialization is allowed!
    if(declaration.template_type().template_parameters().empty())
    {
      convert_template_function_or_member_specialization(declaration);
      return;
    }

    // Explicit qualification is forbidden for function templates,
    // which we can use to distinguish them.

    assert(declaration.declarators().size()>=1);

    cpp_declaratort &declarator=declaration.declarators()[0];
    const cpp_namet &cpp_name=to_cpp_name(declarator.add(ID_name));

    if(cpp_name.is_qualified() ||
       cpp_name.has_template_args())
      return typecheck_class_template_member(declaration);

    // must be function template  
    typecheck_function_template(declaration);
    return;
  }
}
void cpp_typecheckt::typecheck_friend_declaration(
  symbolt &symbol,
  cpp_declarationt &declaration)
{
  // A friend of a class can be a function/method,
  // or a struct/class/union type.

  if(declaration.is_template())
  {
    return; // TODO
    err_location(declaration.type().location());
    str << "friend template not supported";
    throw 0;
  }
  
  // we distinguish these whether there is a declarator
  if(declaration.declarators().empty())
  {
    typet &ftype=declaration.type();

    // must be struct or union
    if(ftype.id()!=ID_struct && ftype.id()!=ID_union)
    {
      err_location(declaration.type());
      str << "unexpected friend";
      throw 0;
    }
       
    if(ftype.find(ID_body).is_not_nil())
    {
      err_location(declaration.type());
      str << "friend declaration must not have compound body";
      throw 0;
    }

    // typecheck ftype
    
    // TODO
//    typecheck_type(ftype);
//    assert(ftype.id()==ID_symbol);
//    symbol.type.add("#friends").move_to_sub(ftype);

    return;
  }

  // It should be a friend function.
  // Do the declarators.
  
  Forall_cpp_declarators(sub_it, declaration)
  {
    bool has_value = sub_it->value().is_not_nil();

    if(!has_value)
    {
      // If no value is found, then we jump to the
      // global scope, and we convert the declarator
      // as if it were declared there
      cpp_save_scopet saved_scope(cpp_scopes);
      cpp_scopes.go_to_global_scope();
      cpp_declarator_convertert cpp_declarator_converter(*this);
      const symbolt &conv_symb = cpp_declarator_converter.convert(
          declaration.type(), declaration.storage_spec(),
          declaration.member_spec(), *sub_it);
      exprt symb_expr = cpp_symbol_expr(conv_symb);
      symbol.type.add("#friends").move_to_sub(symb_expr);
    }
    else
    {
      cpp_declarator_convertert cpp_declarator_converter(*this);
      cpp_declarator_converter.is_friend = true;

      declaration.member_spec().set_inline(true);

      const symbolt &conv_symb = cpp_declarator_converter.convert(
        declaration.type(), declaration.storage_spec(),
        declaration.member_spec(), *sub_it);

      exprt symb_expr = cpp_symbol_expr(conv_symb);

      symbol.type.add("#friends").move_to_sub(symb_expr);
    }
  }
void cpp_typecheckt::typecheck_compound_declarator(
  const symbolt &symbol,
  const cpp_declarationt &declaration,
  cpp_declaratort &declarator,
  struct_typet::componentst &components,
  const irep_idt &access,
  bool is_static,
  bool is_typedef,
  bool is_mutable)
{
  bool is_cast_operator=
    declaration.type().id()=="cpp-cast-operator";

  if(is_cast_operator)
  {
    assert(declarator.name().get_sub().size()==2 &&
           declarator.name().get_sub().front().id()==ID_operator);

    typet type=static_cast<typet &>(declarator.name().get_sub()[1]);
    declarator.type().subtype()=type;

    irept name(ID_name);
    name.set(ID_identifier, "("+cpp_type2name(type)+")");
    declarator.name().get_sub().back().swap(name);
  }

  typet final_type=
    declarator.merge_type(declaration.type());

  // this triggers template elaboration
  elaborate_class_template(final_type);

  typecheck_type(final_type);
  
  cpp_namet cpp_name;
  cpp_name.swap(declarator.name());
  
  irep_idt base_name;
  
  if(cpp_name.is_nil())
  {
    // Yes, there can be members without name.
    base_name=irep_idt();
  }
  else if(cpp_name.is_simple_name())
  {
    base_name=cpp_name.get_base_name();
  }
  else
  {
    err_location(cpp_name.location());
    str << "declarator in compound needs to be simple name";
    throw 0;
  }


  bool is_method=!is_typedef && final_type.id()==ID_code;
  bool is_constructor=declaration.is_constructor();
  bool is_destructor=declaration.is_destructor();
  bool is_virtual=declaration.member_spec().is_virtual();
  bool is_explicit=declaration.member_spec().is_explicit();
  bool is_inline=declaration.member_spec().is_inline();

  final_type.set(ID_C_member_name, symbol.name);

  // first do some sanity checks

  if(is_virtual && !is_method)
  {
    err_location(cpp_name.location());
    str << "only methods can be virtual";
    throw 0;
  }

  if(is_inline && !is_method)
  {
    err_location(cpp_name.location());
    str << "only methods can be inlined";
    throw 0;
  }

  if(is_virtual && is_static)
  {
    err_location(cpp_name.location());
    str << "static methods cannot be virtual";
    throw 0;
  }

  if(is_cast_operator && is_static)
  {
    err_location(cpp_name.location());
    str << "cast operators cannot be static`";
    throw 0;
  }

  if(is_constructor && is_virtual)
  {
    err_location(cpp_name.location());
    str << "constructors cannot be virtual";
    throw 0;
  }

  if(!is_constructor && is_explicit)
  {
    err_location(cpp_name.location());
    str << "only constructors can be explicit";
    throw 0;
  }

  if(is_constructor &&
     base_name!=id2string(symbol.base_name))
  {
    err_location(cpp_name.location());
    str << "member function must return a value or void";
    throw 0;
  }

  if(is_destructor &&
     base_name!="~"+id2string(symbol.base_name))
  {
    err_location(cpp_name.location());
    str << "destructor with wrong name";
    throw 0;
  }

  // now do actual work

  struct_typet::componentt component;

  irep_idt identifier=
    language_prefix+
    cpp_scopes.current_scope().prefix+
    id2string(base_name);

  component.set(ID_name, identifier);
  component.type()=final_type;
  component.set(ID_access, access);
  component.set(ID_base_name, base_name);
  component.set(ID_pretty_name, base_name);
  component.location()=cpp_name.location();

  if(cpp_name.is_operator())
  {
    component.set("is_operator", true);
    component.type().set("#is_operator", true);
  }

  if(is_cast_operator)
    component.set("is_cast_operator", true);

  if(declaration.member_spec().is_explicit())
    component.set("is_explicit", true);

  typet &method_qualifier=
    (typet &)declarator.add("method_qualifier");

  if(is_static)
  {
    component.set(ID_is_static, true);
    component.type().set("#is_static", true);
  }

  if(is_typedef)
    component.set("is_type", true);

  if(is_mutable)
    component.set("is_mutable", true);

  exprt &value=declarator.value();
  irept &initializers=declarator.member_initializers();

  if(is_method)
  {
    component.set(ID_is_inline, declaration.member_spec().is_inline());

    // the 'virtual' name of the function
    std::string virtual_name=
    component.get_string(ID_base_name)+
      id2string(
        function_identifier(static_cast<const typet &>(component.find(ID_type))));

    if(method_qualifier.id()==ID_const)
      virtual_name += "$const";

    if(component.type().get(ID_return_type) == ID_destructor)
      virtual_name= "@dtor";
    
    // The method may be virtual implicitly.
    std::set<irep_idt> virtual_bases;

    for(struct_typet::componentst::const_iterator
        it=components.begin();
        it!=components.end();
        it++)
    {
      if(it->get_bool("is_virtual"))
      {
        if(it->get("virtual_name")==virtual_name)
        {
          is_virtual=true;
          const code_typet& code_type = to_code_type(it->type());
          assert(code_type.arguments().size()>0);
          const typet& pointer_type = code_type.arguments()[0].type();
          assert(pointer_type.id() == ID_pointer);
          virtual_bases.insert(pointer_type.subtype().get(ID_identifier));
        }
      }
    }

    if(!is_virtual)
    {
      typecheck_member_function(
        symbol.name, component, initializers,
        method_qualifier, value);

      if(!value.is_nil() && !is_static)
      {
        err_location(cpp_name.location());
        str << "no initialization allowed here";
        throw 0;
      }
    }
    else // virtual
    {
      component.type().set("#is_virtual", true);
      component.type().set("#virtual_name",virtual_name);

      // Check if it is a pure virtual method
      if(is_virtual)
      {
        if(value.is_not_nil() && value.id() == ID_constant)
        {
          mp_integer i;
          to_integer(value, i);
          if(i!=0)
          {
            err_location(declarator.name().location());
            str << "expected 0 to mark pure virtual method, got " << i;
          }
          component.set("is_pure_virtual", true);
          value.make_nil();
        }
      }

      typecheck_member_function(
        symbol.name,
        component,
        initializers,
        method_qualifier,
        value);

      // get the virtual-table symbol type
      irep_idt vt_name = "virtual_table::"+symbol.name.as_string();

      contextt::symbolst::iterator vtit =
        context.symbols.find(vt_name);

      if(vtit == context.symbols.end())
      {
        // first time: create a virtual-table symbol type 
        symbolt vt_symb_type;
        vt_symb_type.name= vt_name;
        vt_symb_type.base_name="virtual_table::"+symbol.base_name.as_string();
        vt_symb_type.pretty_name = vt_symb_type.base_name;
        vt_symb_type.mode=ID_cpp;
        vt_symb_type.module=module;
        vt_symb_type.location=symbol.location;
        vt_symb_type.type = struct_typet();
        vt_symb_type.type.set(ID_name, vt_symb_type.name);
        vt_symb_type.is_type = true;

        bool failed = context.move(vt_symb_type);
        assert(!failed);
        vtit = context.symbols.find(vt_name);

        // add a virtual-table pointer 
        struct_typet::componentt compo;
        compo.type() = pointer_typet(symbol_typet(vt_name));
        compo.set_name(symbol.name.as_string() +"::@vtable_pointer");
        compo.set(ID_base_name, "@vtable_pointer");
        compo.set(ID_pretty_name, symbol.base_name.as_string() +"@vtable_pointer");
        compo.set("is_vtptr", true);
        compo.set(ID_access, ID_public);
        components.push_back(compo);
        put_compound_into_scope(compo);
      }
      
      assert(vtit->second.type.id()==ID_struct);

      struct_typet &virtual_table=
        to_struct_type(vtit->second.type);

      component.set("virtual_name", virtual_name);
      component.set("is_virtual", is_virtual);

      // add an entry to the virtual table
      struct_typet::componentt vt_entry;
      vt_entry.type() = pointer_typet(component.type());
      vt_entry.set_name(vtit->first.as_string()+"::"+virtual_name);
      vt_entry.set(ID_base_name, virtual_name);
      vt_entry.set(ID_pretty_name, virtual_name);
      vt_entry.set(ID_access, ID_public);
      vt_entry.location() = symbol.location;
      virtual_table.components().push_back(vt_entry);

      // take care of overloading
      while(!virtual_bases.empty())
      {
        irep_idt virtual_base = *virtual_bases.begin();

        // a new function that does 'late casting' of the 'this' parameter
        symbolt func_symb;
        func_symb.name=component.get_name().as_string() + "::" +virtual_base.as_string();
        func_symb.base_name=component.get(ID_base_name);
        func_symb.pretty_name = component.get(ID_base_name);
        func_symb.mode=ID_cpp;
        func_symb.module=module;
        func_symb.location=component.location();
        func_symb.type=component.type();

        // change the type of the 'this' pointer
        code_typet& code_type = to_code_type(func_symb.type);
        code_typet::argumentt& arg= code_type.arguments().front();
        arg.type().subtype().set(ID_identifier, virtual_base);

        // create symbols for the arguments
        code_typet::argumentst& args =  code_type.arguments();
        for(unsigned i=0; i<args.size(); i++)
        {
          code_typet::argumentt& arg = args[i];
          irep_idt base_name = arg.get_base_name();

          if(base_name==irep_idt())
            base_name="arg"+i2string(i);

          symbolt arg_symb;
          arg_symb.name = func_symb.name.as_string() + "::"+ base_name.as_string();
          arg_symb.base_name = base_name;
          arg_symb.pretty_name = base_name;
          arg_symb.mode=ID_cpp;
          arg_symb.location=func_symb.location;
          arg_symb.type = arg.type();

          arg.set(ID_C_identifier, arg_symb.name);

          // add the argument to the symbol table
          bool failed = context.move(arg_symb);
          assert(!failed);
        }

        // do the body of the function
        typecast_exprt late_cast(to_code_type(component.type()).arguments()[0].type());

        late_cast.op0()=
          symbol_expr(namespacet(context).lookup(
            args[0].get(ID_C_identifier)));
        
        if(code_type.return_type().id()!=ID_empty &&
           code_type.return_type().id()!=ID_destructor)
        {
          side_effect_expr_function_callt expr_call;
          expr_call.function() = symbol_exprt(component.get_name(),component.type());
          expr_call.type() = to_code_type(component.type()).return_type();
          expr_call.arguments().reserve(args.size());
          expr_call.arguments().push_back(late_cast);

          for(unsigned i=1; i < args.size(); i++)
          {
            expr_call.arguments().push_back(
              symbol_expr(namespacet(context).lookup(
                args[i].get(ID_C_identifier))));
          }

          code_returnt code_return;
          code_return.return_value() = expr_call;

          func_symb.value = code_return;
        }
        else
        {
          code_function_callt code_func;
          code_func.function() = symbol_exprt(component.get_name(),component.type());
          code_func.arguments().reserve(args.size());
          code_func.arguments().push_back(late_cast);

          for(unsigned i=1; i < args.size(); i++)
          {
            code_func.arguments().push_back(
              symbol_expr(namespacet(context).lookup(
                args[i].get(ID_C_identifier))));
          }

          func_symb.value = code_func;
        }

        // add this new function to the list of components
        
        struct_typet::componentt new_compo = component;
        new_compo.type() = func_symb.type;
        new_compo.set_name(func_symb.name);
        components.push_back(new_compo);

        // add the function to the symbol table
        {
          bool failed = context.move(func_symb);
          assert(!failed);
        }

        // next base
        virtual_bases.erase(virtual_bases.begin());
      }
    }
  }
  
  if(is_static && !is_method) // static non-method member
  {
    // add as global variable to context
    symbolt static_symbol;
    static_symbol.mode=symbol.mode;
    static_symbol.name=identifier;
    static_symbol.type=component.type();
    static_symbol.base_name=component.get(ID_base_name);
    static_symbol.lvalue=true;
    static_symbol.static_lifetime=true;
    static_symbol.location=cpp_name.location();
    static_symbol.is_extern=true;
    
    // TODO: not sure about this: should be defined separately!
    dynamic_initializations.push_back(static_symbol.name);

    symbolt *new_symbol;
    if(context.move(static_symbol, new_symbol))
    {
      err_location(cpp_name.location());
	str << "redeclaration of static member `" 
	    << static_symbol.base_name.as_string()
	    << "'";
      throw 0;
    }

    if(value.is_not_nil())
    {
      if(cpp_is_pod(new_symbol->type))
      {
        new_symbol->value.swap(value);
        c_typecheck_baset::do_initializer(*new_symbol);

        // these are macros if they are PODs and come with a (constant) value
        if(new_symbol->type.get_bool(ID_C_constant))
        {
          simplify(new_symbol->value, *this);
          new_symbol->is_macro=true;
        }
      }
      else
      {
        symbol_exprt symexpr;
        symexpr.set_identifier(new_symbol->name);

        exprt::operandst ops;
        ops.push_back(value);
        codet defcode =
          cpp_constructor(locationt(), symexpr, ops);

        new_symbol->value.swap(defcode);
      }
    }
  }

  // array members must have fixed size
  check_fixed_size_array(component.type());

  put_compound_into_scope(component);

  components.push_back(component);
}