/** * \file init_handler.cpp * \brief Initializers * \author Stefan Reuther * * This file provides a mechanism to parse initializers for variables, * aggregates as well as direct-initialisation. */ INTERFACE: #include #include "type_rep.h" #include "ptree_nodes.h" class Abstract_scope; class Init_reader; class Class_symbol; class Init_handler { Ptree* tree; Abstract_scope* scope; bool zero_init; }; IMPLEMENTATION: #include #include #include "scope.h" #include "except.h" #include "expr_result.h" #include "expr_annotator.h" #include "source.h" #include "implicit_conversion.h" #include "overload_resolver.h" #include "downcast.h" #include "symbol_name.h" #include "class.h" /****************************** Init_reader ******************************/ /** A source of initializers. An Init_reader contains the contents of an initializer brace, and provides it one-at-a-time. */ class Init_reader { Ptree* tree; }; /** A reader reading /tree/. The tree is a list of initializers. It may be null to make an Init_reader which behaves as if it didn't contain anything (i.e. "{ }"). */ PUBLIC Init_reader::Init_reader(Ptree* tree) : tree(tree) { } /** true iff next get() will return a compound. */ PUBLIC bool Init_reader::is_compound() { return tree && dynamic_cast(tree->Car()); } /** Get next initializer. Null if none. */ PUBLIC Ptree* Init_reader::get() { if (tree) { Ptree* rv = tree->Car(); if ((tree = tree->Cdr())) tree = tree->Cdr(); return rv; } else return 0; } /** Like get(), but doesn't remove the element from the list. */ PUBLIC Ptree* Init_reader::get_nondestructive() const { return tree ? tree->Car() : 0; } /** False iff we still have more initializers. */ PUBLIC inline bool Init_reader::empty() { return !tree; } PUBLIC void Init_reader::dump(std::ostream& os) { tree->Display2(os); } /******************************* Init_maker ******************************/ /** Helper class for building brace initializers. */ class Init_maker { Ptree* tree; }; PUBLIC Init_maker::Init_maker() : tree(0) { } /** Add an initializer. */ PUBLIC void Init_maker::add(Ptree* p) { if (tree) { Ptree::Snoc(tree, make_static_leaf(",")); Ptree::Snoc(tree, p); } else { tree = Ptree::List(p); } } /** Make a tree with the initializers gathered so far. */ PUBLIC Ptree* Init_maker::make_init() const { return new PtreeBrace(make_static_leaf("{"), Ptree::List(tree, make_static_leaf("}"))); } /****************************** Init_handler ******************************/ /** Create an Init_handler. \param scope the scope \param tree the tree. Either [= expr] or [( arglist )]. \param zero_init true iff we're doing default/zero initialisation, false if not. This is true for static variables etc., and false for automatic variables. */ PUBLIC Init_handler::Init_handler(Abstract_scope* scope, Ptree* tree, bool zero_init) : tree(tree), scope(scope), zero_init(zero_init) { } PUBLIC Init_handler::~Init_handler() { } /** Extract string literal. If t is a (possibly parenthesized) string literal, return pointer to that literal. Returns null otherwise. */ static Ptree* get_string_literal(Ptree* t) { if (!t) { return 0; } else if (dynamic_cast(t)) { return get_string_literal(t->Second()); } else if (t->IsLeaf()) { const char* c = t->GetPosition(); int l = t->GetLength(); if (l >= 2 && (*c == '"' || ((*c == 'L' || *c == 'l') && c[1] == '"'))) return t; else return 0; } else { return 0; } } /** True iff t is a character type (according to 8.5.2p1). */ static bool is_character_type(Type t) { return t.is_same_unqualified_type(char_type) || t.is_same_unqualified_type(wchar_type) || t.is_same_unqualified_type(uchar_type) || t.is_same_unqualified_type(schar_type); } /** Build a constructor call for type T, with given args. */ PUBLIC Ptree* Init_handler::make_constructor_call(Type t, Ptree* args) { if (t.is_class_type()) { /* look for a constructor */ Class_symbol* csym = downcast(t.get_type_symbol()); Function_symbol* fsym = dynamic_cast(csym->lookup_helper(Symbol_name::CONSTRUCTOR_NAME).untag); if (!fsym) compile_error("class `" + csym->get_name() + "' has no ctor (huh?)"); Overload_resolver resolver(false /* no operator */); Expr_annotator(scope, &Source::instance()).add_parms_from_arglist(&resolver, args); resolver.add_function(fsym, false); bool is_ambig; Overload_candidate* cand = resolver.get_best(&is_ambig); if (is_ambig) compile_error("initialisation is ambiguous"); else if (!cand) compile_error("invalid parameter list for constructor"); Annotated_funcall_maker afm(make_name(cand->fsig), t); for (unsigned i = 0; i < resolver.get_arg_count(); ++i) afm.add_arg(resolver.get_arg(i).get_tree()); return afm.make_funcall(); } else if (t.is_scalar_type() || t.get_kind() == Type::k_Reference) { Ptree* arg; if (!args) if (t.get_kind() == Type::k_Reference) compile_error("can't default-initialize a reference"); else arg = make_static_leaf("0"); else if (args->Length() == 1) arg = args->First(); else compile_error("expression in initializer for scalar or reference type must have only one expression"); Expr_result r = Expr_annotator(scope, &Source::instance()).visit(arg); Implicit_conversion* ics = generate_implicit_conversion(r, t, 0, true /* with user-def */, false /* not copy-init */, false /* not IOA */); if (!ics) compile_error("type mismatch for initializer"); /* turn it into a copy-initializer */ return ics->make_tree(r).get_tree(); } else compile_error("can't direct-initialize that"); } /** Main entry point. Parse our tree as an initializer for type T. Returns the initializer: - a brace list for aggregates - a string literal for char arrays - an expression for others - a constructor call for classes */ PUBLIC Ptree* Init_handler::process_initializer(Type t) { /* shortcut: if we're given an empty tree, get default initializer */ if (!tree) return get_default_initializer_for_type(t); /* The initializer can have the form [= expression] [= { brace }] [( args )] */ if (tree->First()->Eq('(')) { /* (pseudo) constructor call */ Ptree* args = tree->Second(); expect_ptree(tree->Third(), ')'); return make_constructor_call(t, args); } else if (tree->First()->Eq('=')) { /* assignment */ if (dynamic_cast(tree->Second())) { if (t.get_kind() == Type::k_Array || t.is_class_type() || t.is_scalar_type()) { /* scalar, array or class */ NonLeaf tmp(tree->Second(), 0); Init_reader r(&tmp); return process_brace(t, &r); } else compile_error("can't brace-initialize that"); } else { /* copy-assignment */ if (t.get_kind() == Type::k_Array) { if (is_character_type(t.get_basis_type())) { Ptree* ct = get_string_literal(tree->Second()); if (ct) return new Annotated(t, 0, *downcast(ct)); } compile_error("can't copy-initialize that"); } NonLeaf tmp(tree->Second(), 0); Init_reader r(&tmp); return process_brace(t, &r); } } else bogus_ptree_error("strange initializer", tree); } /** Process a brace initializer. \param t target type \param reader source for initializers */ PRIVATE Ptree* Init_handler::process_brace(Type t, Init_reader* reader) { if (t.is_class_type()) { Class_symbol* csym = downcast(t.get_type_symbol()); if (csym->is_aggregate()) { if (reader->is_compound()) { Init_reader sub(reader->get()->Second()); Ptree* rv = process_class(csym, &sub); if (sub.get_nondestructive()) compile_error("too many initializers for type `" + csym->get_name() + "'"); return rv; } else return process_class(csym, reader); } /* fall through to normal conversion */ } else if (t.get_kind() == Type::k_Array) { Type base = t.get_basis_type(); if (is_character_type(base)) { /* might be initialisation with string literal */ Ptree* tree; if (reader->is_compound()) { Init_reader sub(reader->get_nondestructive()->Second()); tree = sub.get(); if (sub.get()) tree = 0; else tree = get_string_literal(tree); } else tree = get_string_literal(reader->get_nondestructive()); if (tree) { /* is initialisation with string literal */ reader->get(); assert(tree->IsLeaf()); return new Annotated(t, 0, *downcast(tree)); } } if (reader->is_compound()) { Init_reader sub(reader->get()->Second()); Ptree* rv = process_array(t.get_basis_type(), &sub); if (sub.get()) /* can not happen currently */ compile_error("too many initializers for array"); return rv; } else { compile_warning("partially bracketed initializer for array; interpretation might be wrong", reader->get_nondestructive()); return process_array(t.get_basis_type(), reader); } } /* here, we expect a scalar or a simple conversion, i.e. 'std::string foo[] = { "bar", "blub" }' */ Ptree* ele; if (reader->is_compound()) { if (!t.is_scalar_type()) compile_error("only scalar types can be initialized with braced value"); Init_reader sub(reader->get()->Second()); ele = sub.get(); if (!ele || sub.get()) compile_error("initializer for non-aggregate must contain exactly one element"); } else ele = reader->get(); if (!ele) { return get_default_initializer_for_type(t); } else { Expr_result res = Expr_annotator(scope, &Source::instance()).visit(ele); Implicit_conversion* ics = generate_implicit_conversion(res, t, 0, true, true, false); if (!ics) compile_error("type mismatch"); return ics->make_tree(res).get_tree(); } } PRIVATE Ptree* Init_handler::process_array(Type t, Init_reader* reader) { Init_maker obi; if (reader->empty()) compile_warning("zero-length array initializer?", reader->get_nondestructive()); while (!reader->empty()) obi.add(process_brace(t, reader)); return obi.make_init(); } PRIVATE Ptree* Init_handler::process_class(Class_symbol* csym, Init_reader* reader) { if (!reader->is_compound() && reader->get_nondestructive()) { /* special case: if the argument is convertible to csym, initialize with it. We're here only when the class is an aggregate, in which case its only constructors are the default and copy constructors (i.e., nothing ugly). */ // FIXME: this parses the expression twice when it fails. // Probably not worth bothering with. Expr_result res = Expr_annotator(scope, &Source::instance()).visit(reader->get_nondestructive()); Implicit_conversion* ics = generate_implicit_conversion(res, csym->get_type(), 0, true /* with user */, true /* is copy-init */, false /* is IOA */); if (ics) { // FIXME: encode a copy ctor call? reader->get(); return ics->make_tree(res).get_tree(); } } Init_maker hornbach; for (Class_symbol::members_t::const_iterator i = csym->mem_begin(); i != csym->mem_end(); ++i) { Variable_symbol* vsym = *i; if (vsym->is_member_variable()) { hornbach.add(process_brace(vsym->get_type(), reader)); if (csym->get_kind() == Symbol::k_Union) break; } } return hornbach.make_init(); } /************************** Default Initializer **************************/ /** Return a default initializer for type t. */ PUBLIC Ptree* Init_handler::get_default_initializer_for_type(Type t) { if (t.is_class_type()) { Class_symbol* csym = downcast(t.get_type_symbol()); Function_symbol* fsym = dynamic_cast(csym->lookup_helper(Symbol_name::CONSTRUCTOR_NAME).untag); if (fsym) { /* we have a constructor. Attempt to call it with no args. */ Overload_resolver resolver(false); resolver.add_function(fsym, false); Overload_candidate* cand = resolver.get_best(0); if (cand) return Annotated_funcall_maker(make_name(cand->fsig), t).make_funcall(); } if (csym->is_aggregate()) { /* if it is an aggregate, try brace-initialisation with empty brace. */ Init_reader reader(0); return process_class(csym, &reader); } /* when we're here, it has a constructor but no default constructor */ compile_error("unable to default-initialize that"); } else if (t.get_kind() == Type::k_Array) { /* we can't default-initialize an array, because we don't know the dimensions */ compile_warning("default-initialisation of an array requested", 0); return Init_maker().make_init(); } else if (t.get_kind() == Type::k_Reference) { /* 8.5p5: a program that calls for default-initialisation of a reference is ill-formed */ compile_error("can't default-initialize a reference"); } else if (t.is_scalar_type()) { if (!zero_init) return 0; Leaf tmp_leaf("0", 1); Expr_result r (new Annotated(int_type, 0, tmp_leaf), Expr_result::k_RValue); Implicit_conversion* ics = generate_implicit_conversion(r, t, 0, true /* with user-def */, false /* not copy-init */, false /* not IOA */); if (!ics) compile_error("can't happen: type mismatch for initializer"); return ics->make_tree(r).get_tree(); } else { compile_error("can't default-initialize `" + t.get_human_readable_type() + "'"); } }