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()); } }
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); }
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); } }
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_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); } }