void c_typecheck_baset::typecheck_c_enum_tag_type(c_enum_tag_typet &type) { // It's just a tag. if(type.find(ID_tag).is_nil()) { error().source_location=type.source_location(); error() << "anonymous enum tag without members" << eom; throw 0; } source_locationt source_location=type.source_location(); irept &tag=type.add(ID_tag); irep_idt base_name=tag.get(ID_C_base_name); irep_idt identifier=tag.get(ID_identifier); // is it in the symbol table? symbol_tablet::symbolst::const_iterator s_it= symbol_table.symbols.find(identifier); if(s_it!=symbol_table.symbols.end()) { // Yes. const symbolt &symbol=s_it->second; if(symbol.type.id()!=ID_c_enum && symbol.type.id()!=ID_incomplete_c_enum) { error().source_location=source_location; error() << "use of tag that does not match previous declaration" << eom; throw 0; } } else { // no, add it as an incomplete c_enum typet new_type(ID_incomplete_c_enum); new_type.subtype()=signed_int_type(); // default new_type.add(ID_tag)=tag; symbolt enum_tag_symbol; enum_tag_symbol.is_type=true; enum_tag_symbol.type=new_type; enum_tag_symbol.location=source_location; enum_tag_symbol.is_file_local=true; enum_tag_symbol.base_name=base_name; enum_tag_symbol.name=identifier; symbolt *new_symbol; move_symbol(enum_tag_symbol, new_symbol); } // Clean up resulting type type.remove(ID_tag); type.set_identifier(identifier); }
void c_typecheck_baset::typecheck_symbol(symbolt &symbol) { current_symbol_id=symbol.name; bool is_function=symbol.type.id()==ID_code; const typet &final_type=follow(symbol.type); // set a few flags symbol.is_lvalue=!symbol.is_type && !symbol.is_macro; irep_idt root_name=symbol.base_name; irep_idt new_name=symbol.name; if(symbol.is_file_local) { // file-local stuff -- stays as is // collisions are resolved during linking } else if(symbol.is_extern && !is_function) { // variables marked as "extern" go into the global namespace // and have static lifetime new_name=root_name; symbol.is_static_lifetime=true; } else if(!is_function && symbol.value.id()==ID_code) { err_location(symbol.value); throw "only functions can have a function body"; } // set the pretty name if(symbol.is_type && (final_type.id()==ID_struct || final_type.id()==ID_incomplete_struct)) { symbol.pretty_name="struct "+id2string(symbol.base_name); } else if(symbol.is_type && (final_type.id()==ID_union || final_type.id()==ID_incomplete_union)) { symbol.pretty_name="union "+id2string(symbol.base_name); } else if(symbol.is_type && (final_type.id()==ID_c_enum || final_type.id()==ID_incomplete_c_enum)) { symbol.pretty_name="enum "+id2string(symbol.base_name); } else { symbol.pretty_name=new_name; } // see if we have it already symbol_tablet::symbolst::iterator old_it=symbol_table.symbols.find(symbol.name); if(old_it==symbol_table.symbols.end()) { // just put into symbol_table symbolt *new_symbol; move_symbol(symbol, new_symbol); typecheck_new_symbol(*new_symbol); } else { if(old_it->second.is_type!=symbol.is_type) { err_location(symbol.location); str << "redeclaration of `" << symbol.display_name() << "' as a different kind of symbol"; throw 0; } if(symbol.is_type) typecheck_redefinition_type(old_it->second, symbol); else typecheck_redefinition_non_type(old_it->second, symbol); } }
void c_typecheck_baset::typecheck_function_body(symbolt &symbol) { code_typet &code_type=to_code_type(symbol.type); assert(symbol.value.is_not_nil()); // reset labels labels_used.clear(); labels_defined.clear(); // fix type symbol.value.type()=code_type; // set return type return_type=code_type.return_type(); unsigned anon_counter=0; // Add the parameter declarations into the symbol table. code_typet::parameterst ¶meters=code_type.parameters(); for(code_typet::parameterst::iterator p_it=parameters.begin(); p_it!=parameters.end(); p_it++) { // may be anonymous if(p_it->get_base_name()==irep_idt()) { irep_idt base_name="#anon"+i2string(anon_counter++); p_it->set_base_name(base_name); } // produce identifier irep_idt base_name=p_it->get_base_name(); irep_idt identifier=id2string(symbol.name)+"::"+id2string(base_name); p_it->set_identifier(identifier); parameter_symbolt p_symbol; p_symbol.type=p_it->type(); p_symbol.name=identifier; p_symbol.base_name=base_name; p_symbol.location=p_it->source_location(); symbolt *new_p_symbol; move_symbol(p_symbol, new_p_symbol); } // typecheck the body code typecheck_code(to_code(symbol.value)); // special case for main() if(symbol.name==ID_main) add_argc_argv(symbol); // check the labels for(std::map<irep_idt, source_locationt>::const_iterator it=labels_used.begin(); it!=labels_used.end(); it++) { if(labels_defined.find(it->first)==labels_defined.end()) { err_location(it->second); str << "branching label `" << it->first << "' is not defined in function"; throw 0; } } }
void c_typecheck_baset::add_argc_argv(const symbolt &main_symbol) { const irept &arguments= main_symbol.type.find(ID_arguments); if(arguments.get_sub().size()==0) return; if(arguments.get_sub().size()!=2 && arguments.get_sub().size()!=3) { err_location(main_symbol.location); throw "main expected to have no or two or three arguments"; } symbolt *argc_new_symbol; const exprt &op0=static_cast<const exprt &>(arguments.get_sub()[0]); const exprt &op1=static_cast<const exprt &>(arguments.get_sub()[1]); { symbolt argc_symbol; argc_symbol.base_name="argc"; argc_symbol.name="c::argc'"; argc_symbol.type=op0.type(); argc_symbol.is_static_lifetime=true; argc_symbol.is_lvalue=true; if(argc_symbol.type.id()!=ID_signedbv && argc_symbol.type.id()!=ID_unsignedbv) { err_location(main_symbol.location); str << "argc argument expected to be integer type, but got `" << to_string(argc_symbol.type) << "'"; throw 0; } move_symbol(argc_symbol, argc_new_symbol); } { if(op1.type().id()!=ID_pointer || op1.type().subtype().id()!=ID_pointer) { err_location(main_symbol.location); str << "argv argument expected to be pointer-to-pointer type, " "but got `" << to_string(op1.type()) << "'"; throw 0; } // we make the type of this thing an array of pointers typet argv_type=array_typet(); argv_type.subtype()=op1.type().subtype(); // need to add one to the size -- the array is terminated // with NULL exprt one_expr=from_integer(1, argc_new_symbol->type); exprt size_expr(ID_plus, argc_new_symbol->type); size_expr.copy_to_operands(symbol_expr(*argc_new_symbol), one_expr); argv_type.add(ID_size).swap(size_expr); symbolt argv_symbol; argv_symbol.base_name="argv'"; argv_symbol.name="c::argv'"; argv_symbol.type=argv_type; argv_symbol.is_static_lifetime=true; argv_symbol.is_lvalue=true; symbolt *argv_new_symbol; move_symbol(argv_symbol, argv_new_symbol); } if(arguments.get_sub().size()==3) { symbolt envp_symbol; envp_symbol.base_name="envp'"; envp_symbol.name="c::envp'"; envp_symbol.type=(static_cast<const exprt&>(arguments.get_sub()[2])).type(); envp_symbol.is_static_lifetime=true; symbolt envp_size_symbol, *envp_new_size_symbol; envp_size_symbol.base_name="envp_size"; envp_size_symbol.name="c::envp_size'"; envp_size_symbol.type=op0.type(); // same type as argc! envp_size_symbol.is_static_lifetime=true; move_symbol(envp_size_symbol, envp_new_size_symbol); if(envp_symbol.type.id()!=ID_pointer) { err_location(main_symbol.location); str << "envp argument expected to be pointer type, but got `" << to_string(envp_symbol.type) << "'"; throw 0; } exprt size_expr = symbol_expr(*envp_new_size_symbol); envp_symbol.type.id(ID_array); envp_symbol.type.add(ID_size).swap(size_expr); symbolt *envp_new_symbol; move_symbol(envp_symbol, envp_new_symbol); } }
void c_typecheck_baset::typecheck_compound_type(struct_union_typet &type) { // These get replaced by symbol types later. irep_idt identifier; bool have_body=type.find(ID_components).is_not_nil(); if(type.find(ID_tag).is_nil()) { // Anonymous? Must come with body. assert(have_body); // produce symbol symbolt compound_symbol; compound_symbol.is_type=true; compound_symbol.type=type; compound_symbol.location=type.source_location(); typecheck_compound_body(to_struct_union_type(compound_symbol.type)); std::string typestr=type2name(compound_symbol.type); compound_symbol.base_name="#anon-"+typestr; compound_symbol.name="tag-#anon#"+typestr; identifier=compound_symbol.name; // We might already have the same anonymous union/struct, // and this is simply ok. Note that the C standard treats // these as different types. if(symbol_table.symbols.find(identifier)==symbol_table.symbols.end()) { symbolt *new_symbol; move_symbol(compound_symbol, new_symbol); } } else { identifier=type.find(ID_tag).get(ID_identifier); // does it exist already? symbol_tablet::symbolst::iterator s_it= symbol_table.symbols.find(identifier); if(s_it==symbol_table.symbols.end()) { // no, add new symbol irep_idt base_name=type.find(ID_tag).get(ID_C_base_name); type.remove(ID_tag); type.set(ID_tag, base_name); symbolt compound_symbol; compound_symbol.is_type=true; compound_symbol.name=identifier; compound_symbol.base_name=base_name; compound_symbol.type=type; compound_symbol.location=type.source_location(); compound_symbol.pretty_name=id2string(type.id())+" "+id2string(base_name); typet new_type=compound_symbol.type; if(compound_symbol.type.id()==ID_struct) compound_symbol.type.id(ID_incomplete_struct); else if(compound_symbol.type.id()==ID_union) compound_symbol.type.id(ID_incomplete_union); else assert(false); symbolt *new_symbol; move_symbol(compound_symbol, new_symbol); if(have_body) { typecheck_compound_body(to_struct_union_type(new_type)); new_symbol->type.swap(new_type); } } else { // yes, it exists already if(s_it->second.type.id()==ID_incomplete_struct || s_it->second.type.id()==ID_incomplete_union) { // Maybe we got a body now. if(have_body) { irep_idt base_name=type.find(ID_tag).get(ID_C_base_name); type.remove(ID_tag); type.set(ID_tag, base_name); typecheck_compound_body(type); s_it->second.type.swap(type); } } else if(have_body) { error().source_location=type.source_location(); error() << "redefinition of body of `" << s_it->second.pretty_name << "'" << eom; throw 0; } } } symbol_typet symbol_type; symbol_type.add_source_location()=type.source_location(); symbol_type.set_identifier(identifier); c_qualifierst original_qualifiers(type); type.swap(symbol_type); original_qualifiers.write(type); }
void c_typecheck_baset::typecheck_c_enum_type(typet &type) { // These come with the declarations // of the enum constants as operands. exprt &as_expr=static_cast<exprt &>(static_cast<irept &>(type)); source_locationt source_location=type.source_location(); // We allow empty enums in the grammar to get better // error messages. if(as_expr.operands().empty()) { error().source_location=source_location; error() << "empty enum" << eom; throw 0; } // enums start at zero; // we also track min and max to find a nice base type mp_integer value=0, min_value=0, max_value=0; std::list<c_enum_typet::c_enum_membert> enum_members; // We need to determine a width, and a signedness // to obtain an 'underlying type'. // We just do int, but gcc might pick smaller widths // if the type is marked as 'packed'. // gcc/clang may also pick a larger width. Visual Studio doesn't. for(auto &op : as_expr.operands()) { ansi_c_declarationt &declaration=to_ansi_c_declaration(op); exprt &v=declaration.declarator().value(); if(v.is_not_nil()) // value given? { exprt tmp_v=v; typecheck_expr(tmp_v); add_rounding_mode(tmp_v); simplify(tmp_v, *this); if(tmp_v.is_true()) value=1; else if(tmp_v.is_false()) value=0; else if(!to_integer(tmp_v, value)) { } else { error().source_location=v.source_location(); error() << "enum is not a constant"; throw 0; } } if(value<min_value) min_value=value; if(value>max_value) max_value=value; typet constant_type= enum_constant_type(min_value, max_value); v=from_integer(value, constant_type); declaration.type()=constant_type; typecheck_declaration(declaration); irep_idt base_name= declaration.declarator().get_base_name(); irep_idt identifier= declaration.declarator().get_name(); // store c_enum_typet::c_enum_membert member; member.set_identifier(identifier); member.set_base_name(base_name); member.set_value(integer2string(value)); enum_members.push_back(member); // produce value for next constant ++value; } // Remove these now; we add them to the // c_enum symbol later. as_expr.operands().clear(); bool is_packed=type.get_bool(ID_C_packed); // tag? if(type.find(ID_tag).is_nil()) { // None, it's anonymous. We generate a tag. std::string anon_identifier="#anon_enum"; for(const auto &member : enum_members) { anon_identifier+='$'; anon_identifier+=id2string(member.get_base_name()); anon_identifier+='='; anon_identifier+=id2string(member.get_value()); } if(is_packed) anon_identifier+="#packed"; type.add(ID_tag).set(ID_identifier, anon_identifier); } irept &tag=type.add(ID_tag); irep_idt base_name=tag.get(ID_C_base_name); irep_idt identifier=tag.get(ID_identifier); // Put into symbol table symbolt enum_tag_symbol; enum_tag_symbol.is_type=true; enum_tag_symbol.type=type; enum_tag_symbol.location=source_location; enum_tag_symbol.is_file_local=true; enum_tag_symbol.base_name=base_name; enum_tag_symbol.name=identifier; // throw in the enum members as 'body' irept::subt &body=enum_tag_symbol.type.add(ID_body).get_sub(); for(const auto &member : enum_members) body.push_back(member); // We use a subtype to store the underlying type. typet underlying_type= enum_underlying_type(min_value, max_value, is_packed); enum_tag_symbol.type.subtype()=underlying_type; // is it in the symbol table already? symbol_tablet::symbolst::iterator s_it= symbol_table.symbols.find(identifier); if(s_it!=symbol_table.symbols.end()) { // Yes. symbolt &symbol=s_it->second; if(symbol.type.id()==ID_incomplete_c_enum) { // Ok, overwrite the type in the symbol table. // This gives us the members and the subtype. symbol.type=enum_tag_symbol.type; } else if(symbol.type.id()==ID_c_enum) { // We might already have the same anonymous enum, and this is // simply ok. Note that the C standard treats these as // different types. if(!base_name.empty()) { error().source_location=type.source_location(); error() << "redeclaration of enum tag" << eom; throw 0; } } else { error().source_location=source_location; error() << "use of tag that does not match previous declaration" << eom; throw 0; } } else { symbolt *new_symbol; move_symbol(enum_tag_symbol, new_symbol); } // We produce a c_enum_tag as the resulting type. type.id(ID_c_enum_tag); type.remove(ID_tag); type.set(ID_identifier, identifier); }