/*******************************************************************\
Module: C++ Language Type Checking
Author: Daniel Kroening, [email protected]
\*******************************************************************/
/// \file
/// C++ Language Type Checking
#include
#include
#include
#include
#include
#include "cpp_enum_type.h"
#include "cpp_typecheck.h"
void cpp_typecheckt::typecheck_enum_body(symbolt &enum_symbol)
{
c_enum_typet &c_enum_type=to_c_enum_type(enum_symbol.type);
exprt &body=static_cast(c_enum_type.add(ID_body));
irept::subt &components=body.get_sub();
c_enum_tag_typet enum_tag_type(enum_symbol.name);
mp_integer i=0;
for(auto &component : components)
{
const irep_idt &name = component.get(ID_name);
if(component.find(ID_value).is_not_nil())
{
exprt &value = static_cast(component.add(ID_value));
typecheck_expr(value);
implicit_typecast(value, c_enum_type.underlying_type());
make_constant(value);
if(to_integer(to_constant_expr(value), i))
{
error().source_location=value.find_source_location();
error() << "failed to produce integer for enum constant" << eom;
throw 0;
}
}
exprt value_expr = from_integer(i, c_enum_type.underlying_type());
value_expr.type()=enum_tag_type; // override type
symbolt symbol{
id2string(enum_symbol.name) + "::" + id2string(name),
enum_tag_type,
enum_symbol.mode};
symbol.base_name=name;
symbol.value=value_expr;
symbol.location = static_cast(
component.find(ID_C_source_location));
symbol.module=module;
symbol.is_macro=true;
symbol.is_file_local = true;
symbol.is_thread_local = true;
symbolt *new_symbol;
if(symbol_table.move(symbol, new_symbol))
{
error().source_location=symbol.location;
error() << "cpp_typecheckt::typecheck_enum_body: "
<< "symbol_table.move() failed" << eom;
throw 0;
}
cpp_idt &scope_identifier=
cpp_scopes.put_into_scope(*new_symbol);
scope_identifier.id_class=cpp_idt::id_classt::SYMBOL;
++i;
}
}
void cpp_typecheckt::typecheck_enum_type(typet &type)
{
// first save qualifiers
c_qualifierst qualifiers;
qualifiers.read(type);
cpp_enum_typet &enum_type=to_cpp_enum_type(type);
bool anonymous=!enum_type.has_tag();
irep_idt base_name;
cpp_save_scopet save_scope(cpp_scopes);
if(anonymous)
{
// we fabricate a tag based on the enum constants contained
base_name=enum_type.generate_anon_tag();
}
else
{
const cpp_namet &tag=enum_type.tag();
cpp_template_args_non_tct template_args;
template_args.make_nil();
cpp_typecheck_resolvet resolver(*this);
resolver.resolve_scope(tag, base_name, template_args);
}
bool has_body=enum_type.has_body();
bool tag_only_declaration=enum_type.get_tag_only_declaration();
cpp_scopet &dest_scope=
tag_scope(base_name, has_body, tag_only_declaration);
const irep_idt symbol_name=
dest_scope.prefix+"tag-"+id2string(base_name);
// check if we have it
symbol_table_baset::symbolst::const_iterator previous_symbol =
symbol_table.symbols.find(symbol_name);
if(previous_symbol!=symbol_table.symbols.end())
{
// we do!
const symbolt &symbol=previous_symbol->second;
if(has_body)
{
error().source_location=type.source_location();
error() << "enum symbol '" << base_name << "' declared previously\n"
<< "location of previous definition: " << symbol.location << eom;
throw 0;
}
}
else if(
has_body ||
config.ansi_c.mode == configt::ansi_ct::flavourt::VISUAL_STUDIO)
{
std::string pretty_name=
cpp_scopes.current_scope().prefix+id2string(base_name);
// C++11 enumerations have an underlying type,
// which defaults to int.
// enums without underlying type may be 'packed'.
if(type.add_subtype().is_nil())
type.add_subtype() = signed_int_type();
else
{
typecheck_type(to_type_with_subtype(type).subtype());
if(
to_type_with_subtype(type).subtype().id() != ID_signedbv &&
to_type_with_subtype(type).subtype().id() != ID_unsignedbv &&
to_type_with_subtype(type).subtype().id() != ID_c_bool)
{
error().source_location=type.source_location();
error() << "underlying type must be integral" << eom;
throw 0;
}
}
type_symbolt symbol{symbol_name, type, ID_cpp};
symbol.base_name=base_name;
symbol.value.make_nil();
symbol.location=type.source_location();
symbol.module=module;
symbol.pretty_name=pretty_name;
// move early, must be visible before doing body
symbolt *new_symbol;
if(symbol_table.move(symbol, new_symbol))
{
error().source_location=symbol.location;
error() << "cpp_typecheckt::typecheck_enum_type: "
<< "symbol_table.move() failed" << eom;
throw 0;
}
// put into scope
cpp_idt &scope_identifier=
cpp_scopes.put_into_scope(*new_symbol, dest_scope);
scope_identifier.id_class=cpp_idt::id_classt::CLASS;
scope_identifier.is_scope = true;
cpp_save_scopet save_scope_before_enum_typecheck(cpp_scopes);
if(new_symbol->type.get_bool(ID_C_class))
cpp_scopes.go_to(scope_identifier);
if(has_body)
typecheck_enum_body(*new_symbol);
}
else
{
error().source_location=type.source_location();
error() << "use of enum '" << base_name << "' without previous declaration"
<< eom;
throw 0;
}
// create enum tag expression, and add the qualifiers
type=c_enum_tag_typet(symbol_name);
qualifiers.write(type);
}