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 ¶meters=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 ¶meters=code_type.parameters(); for(auto ¶m : 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()); }
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); }