void cpp_typecheckt::typecheck_type(typet &type)
{
  assert(!type.id().empty());
  assert(type.is_not_nil());

  try
  {
    cpp_convert_plain_type(type);
  }

  catch(const char *err)
  {
    error().source_location=type.source_location();
    error() << err << eom;
    throw 0;
  }

  catch(const std::string &err)
  {
    error().source_location=type.source_location();
    error() << err << eom;
    throw 0;
  }

  if(type.id()==ID_cpp_name)
  {
    c_qualifierst qualifiers(type);

    cpp_namet cpp_name;
    cpp_name.swap(type);

    exprt symbol_expr=resolve(
      cpp_name,
      cpp_typecheck_resolvet::wantt::TYPE,
      cpp_typecheck_fargst());

    if(symbol_expr.id()!=ID_type)
    {
      error().source_location=type.source_location();
      error() << "error: expected type" << eom;
      throw 0;
    }

    type=symbol_expr.type();
    assert(type.is_not_nil());

    if(type.get_bool(ID_C_constant))
      qualifiers.is_constant = true;

    qualifiers.write(type);
  }
  else if(type.id()==ID_struct ||
          type.id()==ID_union)
  {
    typecheck_compound_type(to_struct_union_type(type));
  }
  else if(type.id()==ID_pointer)
  {
    // the pointer might have a qualifier, but do subtype first
    typecheck_type(type.subtype());

    // Check if it is a pointer-to-member
    if(type.find("to-member").is_not_nil())
    {
      // these can point either to data members or member functions
      // of a class

      typet &class_object=static_cast<typet &>(type.add("to-member"));

      if(class_object.id()==ID_cpp_name)
      {
        assert(class_object.get_sub().back().id()=="::");
        class_object.get_sub().pop_back();
      }

      typecheck_type(class_object);

      // there may be parameters if this is a pointer to member function
      if(type.subtype().id()==ID_code)
      {
        irept::subt &parameters=type.subtype().add(ID_parameters).get_sub();

        if(parameters.empty() ||
           parameters.front().get(ID_C_base_name)!=ID_this)
        {
          // Add 'this' to the parameters
          exprt a0(ID_parameter);
          a0.set(ID_C_base_name, ID_this);
          a0.type().id(ID_pointer);
          a0.type().subtype() = class_object;
          parameters.insert(parameters.begin(), a0);
        }
      }
    }
  }
  else if(type.id()==ID_array)
  {
    exprt &size_expr=to_array_type(type).size();

    if(size_expr.is_not_nil())
    {
      typecheck_expr(size_expr);
      simplify(size_expr, *this);
    }

    typecheck_type(type.subtype());

    if(type.subtype().get_bool(ID_C_constant))
      type.set(ID_C_constant, true);

    if(type.subtype().get_bool(ID_C_volatile))
      type.set(ID_C_volatile, true);
  }
  else if(type.id()==ID_code)
  {
    code_typet &code_type=to_code_type(type);
    typecheck_type(code_type.return_type());

    code_typet::parameterst &parameters=code_type.parameters();

    for(auto &param : parameters)
    {
      typecheck_type(param.type());

      // see if there is a default value
      if(param.has_default_value())
      {
        typecheck_expr(param.default_value());
        implicit_typecast(param.default_value(), param.type());
      }
    }
  }
  else if(type.id()==ID_template)
  {
    typecheck_type(type.subtype());
  }
  else if(type.id()==ID_c_enum)
  {
    typecheck_enum_type(type);
  }
  else if(type.id()==ID_c_enum_tag)
  {
  }
  else if(type.id()==ID_c_bit_field)
  {
    typecheck_c_bit_field_type(to_c_bit_field_type(type));
  }
  else if(type.id()==ID_unsignedbv ||
          type.id()==ID_signedbv ||
          type.id()==ID_bool ||
          type.id()==ID_floatbv ||
          type.id()==ID_fixedbv ||
          type.id()==ID_empty)
  {
  }
  else if(type.id()==ID_symbol)
  {
  }
  else if(type.id()==ID_constructor ||
          type.id()==ID_destructor)
  {
  }
  else if(type.id()=="cpp-cast-operator")
  {
  }
  else if(type.id()=="cpp-template-type")
  {
  }
  else if(type.id()==ID_typeof)
  {
    exprt e=static_cast<const exprt &>(type.find(ID_expr_arg));

    if(e.is_nil())
    {
      typet tmp_type=
        static_cast<const typet &>(type.find(ID_type_arg));

      if(tmp_type.id()==ID_cpp_name)
      {
        // this may be ambiguous -- it can be either a type or
        // an expression

        cpp_typecheck_fargst fargs;

        exprt symbol_expr=resolve(
          to_cpp_name(static_cast<const irept &>(tmp_type)),
          cpp_typecheck_resolvet::wantt::BOTH,
          fargs);

        type=symbol_expr.type();
      }
      else
      {
        typecheck_type(tmp_type);
        type=tmp_type;
      }
    }
    else
    {
      typecheck_expr(e);
      type=e.type();
    }
  }
  else if(type.id()==ID_decltype)
  {
    exprt e=static_cast<const exprt &>(type.find(ID_expr_arg));
    typecheck_expr(e);
    type=e.type();
  }
  else if(type.id()==ID_unassigned)
  {
    // ignore, for template parameter guessing
  }
  else if(type.id()==ID_template_class_instance)
  {
    // ok (internally generated)
  }
  else if(type.id()==ID_block_pointer)
  {
    // This is an Apple extension for lambda-like constructs.
    // http://thirdcog.eu/pwcblocks/
  }
  else if(type.id()==ID_nullptr)
  {
  }
  else
  {
    error().source_location=type.source_location();
    error() << "unexpected cpp type: " << type.pretty() << eom;
    throw 0;
  }

  assert(type.is_not_nil());
}
Example #2
0
void cpp_typecheckt::typecheck_type(typet &type)
{
  assert(type.id() != "");
  assert(type.is_not_nil());

  try
  {
    cpp_convert_plain_type(type);
  }

  catch(const char *error)
  {
    err_location(type);
    str << error;
    throw 0;
  }

  catch(const std::string &error)
  {
    err_location(type);
    str << error;
    throw 0;
  }

  if(type.id() == "cpp-name")
  {
    c_qualifierst qualifiers(type);

    cpp_namet cpp_name;
    cpp_name.swap(type);

    exprt symbol_expr =
      resolve(cpp_name, cpp_typecheck_resolvet::TYPE, cpp_typecheck_fargst());

    if(symbol_expr.id() != "type")
    {
      err_location(type);
      str << "error: expected type";
      throw 0;
    }

    type = symbol_expr.type();
    assert(type.is_not_nil());

    if(type.cmt_constant())
      qualifiers.is_constant = true;

    qualifiers.write(type);
  }
  else if(type.id() == "struct" || type.id() == "union")
  {
    typecheck_compound_type(type);
  }
  else if(type.id() == "pointer")
  {
    // the pointer might have a qualifier, but do subtype first
    typecheck_type(type.subtype());

    // Check if it is a pointer-to-member
    if(type.find("to-member").is_not_nil())
    {
      // these can point either to data members or member functions
      // of a class

      typet &class_object = static_cast<typet &>(type.add("to-member"));

      if(class_object.id() == "cpp-name")
      {
        assert(class_object.get_sub().back().id() == "::");
        class_object.get_sub().pop_back();
      }

      typecheck_type(class_object);

      // there may be arguments if this is a pointer to member function
      if(type.subtype().id() == "code")
      {
        irept::subt &args = type.subtype().add("arguments").get_sub();

        if(args.empty() || args.front().cmt_base_name() != "this")
        {
          // Add 'this' to the arguments
          exprt a0("argument");
          a0.cmt_base_name("this");
          a0.type().id("pointer");
          a0.type().subtype() = class_object;
          args.insert(args.begin(), a0);
        }
      }
    }

    // now do qualifier
    if(type.find("#qualifier").is_not_nil())
    {
      typet &t = static_cast<typet &>(type.add("#qualifier"));
      cpp_convert_plain_type(t);
      c_qualifierst q(t);
      q.write(type);
    }

    type.remove("#qualifier");
  }
  else if(type.id() == "array")
  {
    exprt &size_expr = to_array_type(type).size();

    if(size_expr.is_nil())
      type.id("incomplete_array");
    else
      typecheck_expr(size_expr);

    // TODO: If is a incomplete_array, it should always
    // have initializers, except for catch declaration

    typecheck_type(type.subtype());

    if(type.subtype().cmt_constant())
      type.cmt_constant(true);

    if(type.subtype().cmt_volatile())
      type.set("#volatile", true);
  }
  else if(type.id() == "code")
  {
    code_typet &code_type = to_code_type(type);
    typecheck_type(code_type.return_type());

    code_typet::argumentst &arguments = code_type.arguments();

    for(auto &argument : arguments)
    {
      typecheck_type(argument.type());

      // see if there is a default value
      if(argument.has_default_value())
      {
        typecheck_expr(argument.default_value());
        implicit_typecast(argument.default_value(), argument.type());
      }
    }
  }
  else if(type.id() == "template")
  {
    typecheck_type(type.subtype());
  }
  else if(type.id() == "c_enum")
  {
    typecheck_enum_type(type);
  }
  else if(
    type.id() == "unsignedbv" || type.id() == "signedbv" ||
    type.id() == "bool" || type.id() == "floatbv" || type.id() == "fixedbv" ||
    type.id() == "empty")
  {
  }
  else if(type.id() == "symbol")
  {
  }
  else if(type.id() == "constructor" || type.id() == "destructor")
  {
  }
  else if(type.id() == "cpp-cast-operator")
  {
  }
  else if(type.id() == "cpp-template-type")
  {
  }
  else if(type.id() == "typeof")
  {
    exprt e = static_cast<const exprt &>(type.find("expr"));

    if(e.is_nil())
    {
      typet tmp_type = static_cast<const typet &>(type.find("sizeof-type"));

      if(tmp_type.id() == "cpp-name")
      {
        // this may be ambiguous -- it can be either a type or
        // an expression

        cpp_typecheck_fargst fargs;

        exprt symbol_expr = resolve(
          to_cpp_name(static_cast<const irept &>(tmp_type)),
          cpp_typecheck_resolvet::BOTH,
          fargs);

        type = symbol_expr.type();
      }
      else
      {
        typecheck_type(tmp_type);
        type = tmp_type;
      }
    }
    else
    {
      typecheck_expr(e);
      type = e.type();
    }
  }
  else if(type.id() == "decltype")
  {
    exprt e = static_cast<const exprt &>(type.find("expr_arg"));
    typecheck_expr(e);
    type = e.type();
  }
  else if(type.id() == "unassigned")
  {
    // ignore, for template argument guessing
  }
  else if(type.id() == "ellipsis")
  {
  }
  else
  {
    err_location(type);
    str << "unexpected type: " << type.pretty();
    throw 0;
  }

  assert(type.is_not_nil());
}
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();
  }
}
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;
}
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_compound_type(
  struct_union_typet &type)
{
  // first save qualifiers
  c_qualifierst qualifiers(type);

  // now clear them from the type
  type.remove(ID_C_constant);
  type.remove(ID_C_volatile);
  type.remove(ID_C_restricted);

  // get the tag name
  bool anonymous=type.find(ID_tag).is_nil();
  irep_idt base_name;
  cpp_scopet *dest_scope=NULL;
  bool has_body=type.find(ID_body).is_not_nil();
  bool tag_only_declaration=type.get_bool(ID_C_tag_only_declaration);

  if(anonymous)
  {
    base_name="#anon_"+type.id_string()+i2string(anon_counter++);
    type.set("#is_anonymous", true);
    // anonymous structs always go into the current scope
    dest_scope=&cpp_scopes.current_scope();
  }
  else
  {
    const cpp_namet &cpp_name=
      to_cpp_name(type.find(ID_tag));

    // scope given?
    if(cpp_name.is_simple_name())
    {
      base_name=cpp_name.get_base_name();
      dest_scope=&tag_scope(base_name, has_body, tag_only_declaration);
    }
    else
    {
      cpp_save_scopet cpp_save_scope(cpp_scopes);
      cpp_typecheck_resolvet cpp_typecheck_resolve(*this);
      cpp_template_args_non_tct t_args;
      dest_scope=&cpp_typecheck_resolve.resolve_scope(cpp_name, base_name, t_args);
    }
  }

  const irep_idt symbol_name=
    language_prefix+
    dest_scope->prefix+
    "tag."+id2string(base_name);

  // check if we have it already

  contextt::symbolst::iterator previous_symbol=
    context.symbols.find(symbol_name);

  if(previous_symbol!=context.symbols.end())
  {
    // we do!

    symbolt &symbol=previous_symbol->second;

    if(has_body)
    {
      if(symbol.type.id()=="incomplete_"+type.id_string())
      {
        // a previously incomplete struct/union becomes complete
        symbol.type.swap(type);
        typecheck_compound_body(symbol);
      }
      else
      {
        err_location(type.location());
        str << "error: struct symbol `" << base_name
            << "' declared previously" << std::endl;
        str << "location of previous definition: "
            << symbol.location;
        throw 0;
      }
    }
  }
  else
  {
    // produce new symbol
    symbolt symbol;

    symbol.name=symbol_name;
    symbol.base_name=base_name;
    symbol.value.make_nil();
    symbol.location=type.location();
    symbol.mode=ID_cpp;
    symbol.module=module;
    symbol.type.swap(type);
    symbol.is_type=true;
    symbol.is_macro=false;
    symbol.pretty_name=cpp_scopes.current_scope().prefix+id2string(symbol.base_name);
    symbol.type.set(ID_tag, symbol.pretty_name);

    // move early, must be visible before doing body
    symbolt *new_symbol;
 
    if(context.move(symbol, new_symbol))
      throw "cpp_typecheckt::typecheck_compound_type: context.move() failed";

    // put into dest_scope
    cpp_idt &id=cpp_scopes.put_into_scope(*new_symbol, *dest_scope);

    id.id_class=cpp_idt::CLASS;
    id.is_scope=true;
    id.prefix=cpp_scopes.current_scope().prefix+
              id2string(new_symbol->base_name)+"::";
    id.class_identifier=new_symbol->name;
    id.id_class=cpp_idt::CLASS;

    if(has_body)
      typecheck_compound_body(*new_symbol);
    else
    {
      typet new_type("incomplete_"+new_symbol->type.id_string());
      new_type.set(ID_tag, new_symbol->base_name);
      new_symbol->type.swap(new_type);
    }
  }

  // create type symbol
  typet symbol_type(ID_symbol);
  symbol_type.set(ID_identifier, symbol_name);
  qualifiers.write(symbol_type);
  type.swap(symbol_type);
}