std::string cpp_typecheckt::class_template_identifier( const irep_idt &base_name, const template_typet &template_type, const cpp_template_args_non_tct &partial_specialization_args) { std::string identifier= cpp_scopes.current_scope().prefix+ "template."+id2string(base_name) + "<"; int counter=0; // these are probably not needed -- templates // should be unique in a namespace for(template_typet::template_parameterst::const_iterator it=template_type.template_parameters().begin(); it!=template_type.template_parameters().end(); it++) { if(counter!=0) identifier+=','; if(it->id()==ID_type) identifier+="Type"+i2string(counter); else identifier+="Non_Type"+i2string(counter); counter++; } identifier += ">"; if(!partial_specialization_args.arguments().empty()) { identifier+="_specialized_to_<"; counter=0; for(cpp_template_args_non_tct::argumentst::const_iterator it=partial_specialization_args.arguments().begin(); it!=partial_specialization_args.arguments().end(); it++, counter++) { if(counter!=0) identifier+=','; // These are not yet typechecked, as they may depend // on unassigned template parameters. if(it->id()==ID_type || it->id()=="ambiguous") identifier+=cpp_type2name(it->type()); else identifier+=cpp_expr2name(*it); } identifier+='>'; } return identifier; }
std::string cpp_typecheckt::function_template_identifier( const irep_idt &base_name, const template_typet &template_type, const typet &function_type) { // we first build something without function arguments cpp_template_args_non_tct partial_specialization_args; std::string identifier= class_template_identifier(base_name, template_type, partial_specialization_args); // we must also add the signature of the function to the identifier identifier+=cpp_type2name(function_type); return identifier; }
irep_idt cpp_typecheckt::function_identifier(const typet &type) { const code_typet &function_type= to_code_type(template_subtype(type)); const code_typet::argumentst &arguments= function_type.arguments(); std::string result; bool first=true; result+='('; // the name of the function should not depend on // the class name that is encoded in the type of this, // but we must distinguish "const" and "non-const" member // functions code_typet::argumentst::const_iterator it= arguments.begin(); if(it!=arguments.end() && it->get_identifier()==ID_this) { const typet &pointer=it->type(); const typet &symbol =pointer.subtype(); if(symbol.get_bool(ID_C_constant)) result+="const$"; if(symbol.get_bool(ID_C_volatile)) result+="volatile$"; result+="this"; first=false; it++; } // we skipped the "this", on purpose! for(; it!=arguments.end(); it++) { if(first) first=false; else result+=","; typet tmp_type=it->type(); result+=cpp_type2name(it->type()); } result+=')'; return result; }
symbolt &cpp_declarator_convertert::convert( const typet &declaration_type, const cpp_storage_spect &storage_spec, const cpp_member_spect &member_spec, cpp_declaratort &declarator) { assert(declaration_type.is_not_nil()); if(declaration_type.id()=="cpp-cast-operator") { typet type; type.swap(declarator.name().get_sub().back()); declarator.type().subtype()=type; std::string tmp; cpp_typecheck.typecheck_type(type); irept name(ID_name); name.set(ID_identifier, "("+cpp_type2name(type)+")"); declarator.name().get_sub().back().swap(name); } assert(declarator.id()==ID_cpp_declarator); final_type=declarator.merge_type(declaration_type); assert(final_type.is_not_nil()); cpp_template_args_non_tct template_args; // run resolver on scope { cpp_save_scopet save_scope(cpp_typecheck.cpp_scopes); cpp_typecheck_resolvet cpp_typecheck_resolve(cpp_typecheck); cpp_typecheck_resolve.resolve_scope( declarator.name(), base_name, template_args); scope=&cpp_typecheck.cpp_scopes.current_scope(); // check the declarator-part of the type, in that scope cpp_typecheck.typecheck_type(final_type); } is_code=is_code_type(final_type); // global-scope arrays must have fixed size if(scope->is_global_scope()) cpp_typecheck.check_fixed_size_array(final_type); get_final_identifier(); // first see if it is a member if(scope->id_class==cpp_idt::CLASS && !is_friend) { // it's a member! it must be declared already typet &method_qualifier= static_cast<typet &>(declarator.method_qualifier()); // adjust template type if(final_type.id()==ID_template) { assert(0); typet tmp; tmp.swap(final_type.subtype()); final_type.swap(tmp); } // try static first symbol_tablet::symbolst::iterator c_it= cpp_typecheck.symbol_table.symbols.find(final_identifier); if(c_it==cpp_typecheck.symbol_table.symbols.end()) { // adjust type if it's a non-static member function if(final_type.id()==ID_code) cpp_typecheck.adjust_method_type( scope->identifier, final_type, method_qualifier); get_final_identifier(); // try again c_it=cpp_typecheck.symbol_table.symbols.find(final_identifier); if(c_it==cpp_typecheck.symbol_table.symbols.end()) { cpp_typecheck.err_location(declarator.name()); cpp_typecheck.str << "member `" << base_name << "' not found in scope `" << scope->identifier << "'"; throw 0; } } assert(c_it!=cpp_typecheck.symbol_table.symbols.end()); symbolt &symbol=c_it->second; combine_types(declarator.name().source_location(), final_type, symbol); enforce_rules(symbol); // If it is a constructor, we take care of the // object initialization if(final_type.get(ID_return_type)==ID_constructor) { const cpp_namet &name=declarator.name(); exprt symbol_expr= cpp_typecheck.resolve( name, cpp_typecheck_resolvet::TYPE, cpp_typecheck_fargst()); if(symbol_expr.id()!=ID_type || symbol_expr.type().id()!=ID_symbol) { cpp_typecheck.err_location(name.source_location()); cpp_typecheck.str << "error: expected type"; throw 0; } irep_idt identifier=symbol_expr.type().get(ID_identifier); const symbolt &symb=cpp_typecheck.lookup(identifier); const typet &type = symb.type; assert(type.id()==ID_struct); if(declarator.find(ID_member_initializers).is_nil()) declarator.set(ID_member_initializers, ID_member_initializers); cpp_typecheck.check_member_initializers( type.find(ID_bases), to_struct_type(type).components(), declarator.member_initializers()); cpp_typecheck.full_member_initialization( to_struct_type(type), declarator.member_initializers()); } if(!storage_spec.is_extern()) symbol.is_extern=false; // initializer? handle_initializer(symbol, declarator); return symbol; } else { // no, it's no way a method // we won't allow the constructor/destructor type if(final_type.id()==ID_code && to_code_type(final_type).return_type().id()==ID_constructor) { cpp_typecheck.err_location(declarator.name().source_location()); cpp_typecheck.str << "function must have return type"; throw 0; } // already there? symbol_tablet::symbolst::iterator c_it= cpp_typecheck.symbol_table.symbols.find(final_identifier); if(c_it==cpp_typecheck.symbol_table.symbols.end()) return convert_new_symbol(storage_spec, member_spec, declarator); symbolt &symbol=c_it->second; if(!storage_spec.is_extern()) symbol.is_extern = false; if(declarator.get_bool("#template_case")) return symbol; combine_types(declarator.name().source_location(), final_type, symbol); enforce_rules(symbol); // initializer? handle_initializer(symbol, declarator); if(symbol.type.id()=="cpp-template-type") { cpp_scopet::id_sett id_set; scope->lookup_identifier(symbol.name, cpp_idt::TEMPLATE_PARAMETER, id_set); if(id_set.empty()) { cpp_idt &identifier= cpp_typecheck.cpp_scopes.put_into_scope(symbol,*scope); identifier.id_class=cpp_idt::TEMPLATE_PARAMETER; } } return symbol; } }
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); }