summaryrefslogtreecommitdiff
path: root/odb/odb/processor.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/processor.cxx')
-rw-r--r--odb/odb/processor.cxx3267
1 files changed, 3267 insertions, 0 deletions
diff --git a/odb/odb/processor.cxx b/odb/odb/processor.cxx
new file mode 100644
index 0000000..fb129fa
--- /dev/null
+++ b/odb/odb/processor.cxx
@@ -0,0 +1,3267 @@
+// file : odb/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <iostream>
+#include <algorithm> // std::find
+
+#include <odb/common.hxx>
+#include <odb/lookup.hxx>
+#include <odb/context.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/processor.hxx>
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace
+{
+ // Find name hint for this type decl.
+ //
+ static semantics::names*
+ find_hint (semantics::unit& u, tree decl)
+ {
+ semantics::names* r (0);
+
+ for (tree ot (DECL_ORIGINAL_TYPE (decl));
+ ot != 0;
+ ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
+ {
+ if ((r = u.find_hint (ot)))
+ break;
+
+ decl = TYPE_NAME (ot);
+ }
+
+ return r;
+ }
+
+ // Indirect (dynamic) context values.
+ //
+ static semantics::type*
+ id_tree_type (semantics::names*& hint)
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return &c.utype (id, hint);
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // See if this member is a section.
+ //
+ if (t.fq_name () == "::odb::section")
+ {
+ using semantics::class_;
+
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+ class_* poly_root (polymorphic (c));
+ semantics::data_member* opt (optimistic (c));
+
+ // If we have sections in a polymorphic optimistic hierarchy,
+ // then the version member should be in the root.
+ //
+ if (poly_root == &c && opt != 0 && &opt->scope () != &c)
+ {
+ error (m.location ()) << "version must be a direct data member " <<
+ "of a class that contains sections" << endl;
+ info (opt->location ()) << "version member is declared here" << endl;
+ throw operation_failed ();
+ }
+
+ process_user_section (m, c);
+
+ // We don't need a modifier but the accessor should be by-reference.
+ //
+ process_access (m, "get");
+
+ member_access& ma (m.get<member_access> ("get"));
+ if (ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a section" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // Mark this member as transient since we don't store it in the
+ // database.
+ //
+ m.set ("transient", true);
+
+ features.section = true;
+ return;
+ }
+ else
+ {
+ process_access (m, "get");
+ process_access (m, "set");
+ }
+
+ // See if this member belongs to a section.
+ //
+ if (m.count ("section-member") != 0)
+ process_section_member (m);
+
+ // We don't need to do any further processing for common if we
+ // are generating static multi-database code.
+ //
+ if (multi_static && options.database ()[0] == database::common)
+ return;
+
+ // Handle wrappers.
+ //
+ semantics::type* wt (0), *qwt (0);
+ semantics::names* whint (0);
+ if (process_wrapper (t))
+ {
+ qwt = t.get<semantics::type*> ("wrapper-type");
+ whint = t.get<semantics::names*> ("wrapper-hint");
+ wt = &utype (*qwt, whint);
+ }
+
+ // If the type is const and the member is not id, version, or
+ // inverse, then mark it as readonly. In case of a wrapper,
+ // both the wrapper type and the wrapped type must be const.
+ // To see why, consider these possibilities:
+ //
+ // unique_ptr<const T> - can modify by setting a new pointer
+ // const unique_ptr<T> - can modify by changing the pointed-to value
+ //
+ if (const_member (m) && !(id (m) || version (m) || m.count ("inverse")))
+ {
+ if (qwt == 0 || const_type (*qwt))
+ m.set ("readonly", true);
+ }
+
+ process_points_to (m);
+
+ if (composite_wrapper (t))
+ return;
+
+ // Process object pointer. The resulting column will be a simple
+ // or composite value.
+ //
+ if (process_object_pointer (m, t))
+ return;
+
+ // Before checking if this is a container, check if this member
+ // or its type were deduced to be a simple value based on the
+ // pragmas. This is necessary because a container member (e.g.,
+ // vector<char>) can be "overridden" into a simple value (e.g.,
+ // BLOB) with a pragma.
+ //
+ if (m.count ("simple") ||
+ t.count ("simple") ||
+ (wt != 0 && wt->count ("simple")))
+ return;
+
+ process_container (m, (wt != 0 ? *wt : t));
+ }
+
+ //
+ // Process member access expressions.
+ //
+
+ enum found_type
+ {
+ found_none,
+ found_some, // Found something but keep looking for a better one.
+ found_best
+ };
+
+ // Check if a function is a suitable accessor for this member.
+ //
+ found_type
+ check_accessor (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ // Must be const.
+ //
+ if (!DECL_CONST_MEMFUNC_P (f))
+ return found_none;
+
+ // Accessor is a function with no arguments (other than 'this').
+ //
+ if (FUNCTION_FIRST_USER_PARMTYPE (f) != void_list_node)
+ return found_none;
+
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // In the strict mode make sure the function returns for non-array
+ // types a value or a (const) reference to the member type and for
+ // array types a (const) pointer to element type. In the lax mode
+ // we just check that the return value is not void.
+ //
+ if (strict)
+ {
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+ else if (r == void_type_node)
+ return found_none;
+
+ cxx_tokens& e (ma.expr);
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // See if it returns by value.
+ //
+ ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE);
+
+ return found_best;
+ }
+
+ // Check if a function is a suitable modifier for this member.
+ //
+ found_type
+ check_modifier (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ tree a (FUNCTION_FIRST_USER_PARMTYPE (f));
+
+ // For a modifier, it can either be a function that returns a non-
+ // const reference (or non-const pointer, in case the member is an
+ // array) or a by-value modifier that sets a new value. If both are
+ // available, we prefer the former for efficiency.
+ //
+ cxx_tokens& e (ma.expr);
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (a == void_list_node)
+ {
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // By-reference modifier. Should return a reference or a pointer.
+ //
+ if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE))
+ return found_none;
+
+ // The base type should not be const and, in strict mode, should
+ // match the member type.
+ //
+ tree bt (TREE_TYPE (r));
+
+ if (CP_TYPE_CONST_P (bt))
+ return found_none;
+
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+
+ e.clear (); // Could contain by value modifier.
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ return found_best;
+ }
+ // Otherwise look for a by value modifier, which is a function
+ // with a single argument.
+ //
+ else if (TREE_CHAIN (a) == void_list_node)
+ {
+ // In the lax mode any function with a single argument works
+ // for us. And we don't care what it returns.
+ //
+ if (strict)
+ {
+ // In the strict mode make sure the argument matches the
+ // member. This is exactly the same logic as in accessor
+ // with regards to arrays, references, etc.
+ //
+ tree at (TREE_VALUE (a));
+ gcc_tree_code_type tc (TREE_CODE (at));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // Continue searching in case there is version that returns a
+ // non-const reference which we prefer for efficiency.
+ //
+ return found_some;
+ }
+ else
+ return found_none; // We didn't find anything better.
+ }
+
+ return found_none;
+ }
+
+ void
+ process_access (semantics::data_member& m, std::string const& k)
+ {
+ bool virt (m.count ("virtual"));
+
+ // Ignore certain special virtual members.
+ //
+ if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator")))
+ return;
+
+ char const* kind (k == "get" ? "accessor" : "modifier");
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+
+ // If we don't have an access expression, try to come up with
+ // one.
+ //
+ if (!m.count (k))
+ {
+ found_type found (found_none);
+ semantics::access const& a (m.named ().access ());
+ member_access& ma (
+ m.set (
+ k, member_access (m.location (), kind, true)));
+
+ // If this member is not virtual and is either public or if we
+ // are a friend of this class, then go for the member directly.
+ //
+ if (!virt && (a == semantics::access::public_ ||
+ c.get<bool> ("friend")))
+ {
+ ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ ma.expr.push_back (cxx_token (0, CPP_DOT));
+ ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ()));
+ found = found_best;
+ }
+
+ // Otherwise, try to find a suitable accessor/modifier.
+ //
+
+ // First try the original name. If that doesn't produce anything,
+ // then try the public name.
+ //
+ bool t (k == "get"
+ ? options.accessor_regex_trace ()
+ : options.modifier_regex_trace ());
+ regex_mapping const& re (
+ k == "get" ? accessor_regex : modifier_regex);
+
+ for (unsigned short j (0); found != found_best && j != 2; ++j)
+ {
+ string b (j == 0 ? m.name () : public_name (m, false));
+
+ // Skip the second pass if original and public names are the same.
+ //
+ if (j == 1 && b == m.name ())
+ continue;
+
+ if (t)
+ cerr << kind << (j == 0 ? " original" : " public")
+ << " name '" << b << "'" << endl;
+
+ for (regex_mapping::const_iterator i (re.begin ());
+ found != found_best && i != re.end ();
+ ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (!i->match (b))
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ string n (i->replace (b));
+
+ if (t)
+ cerr << "'" << n << "' : ";
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in public non-static member
+ // functions. Note that TREE_PUBLIC() returns something
+ // other than what we need.
+ //
+ if (
+#if BUILDING_GCC_MAJOR >= 14
+ !DECL_OBJECT_MEMBER_FUNCTION_P (f)
+#else
+ !DECL_NONSTATIC_MEMBER_FUNCTION_P (f)
+#endif
+ || TREE_PRIVATE (f)
+ || TREE_PROTECTED (f))
+ continue;
+
+ found_type r (k == "get"
+ ? check_accessor (m, f, n, ma, true)
+ : check_modifier (m, f, n, ma, true));
+
+ if (r != found_none)
+ {
+ // Update the location of the access expression to point
+ // to this function.
+ //
+ ma.loc = location (real_source_location (f));
+ found = r;
+ }
+ }
+
+ if (t)
+ cerr << (found != found_none ? '+' : '-') << endl;
+ }
+ }
+
+ // If that didn't work then the generated code won't be able
+ // to access this member.
+ //
+ if (found == found_none)
+ {
+ location const& l (m.location ());
+
+ if (virt)
+ {
+ error (l) << "no suitable " << kind << " function could be "
+ << "automatically found for virtual data member '"
+ << m.name () << "'" << endl;
+
+ info (l) << "use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+ else
+ {
+ error (l) << "data member '" << m.name () << "' is "
+ << a.string () << " and no suitable " << kind
+ << " function could be automatically found" << endl;
+
+ info (l) << "consider making class 'odb::access' a friend of "
+ << "class '" << class_name (c) << "'" << endl;
+
+ info (l) << "or use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+
+ throw operation_failed ();
+ }
+ }
+
+ member_access& ma (m.get<member_access> (k));
+
+ if (ma.empty ())
+ return;
+
+ cxx_tokens& e (ma.expr);
+
+ // If it is just a name, resolve it and convert to an appropriate
+ // expression.
+ //
+ if (e.size () == 1 && e.back ().type == CPP_NAME)
+ {
+ string n (e.back ().literal);
+ e.clear ();
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node)
+ {
+ error (ma.loc) << "unable to resolve data member or function "
+ << "name '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case FIELD_DECL:
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ break;
+ }
+ case BASELINK:
+ {
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in non-static member functions.
+ //
+ if (
+#if BUILDING_GCC_MAJOR >= 14
+ !DECL_OBJECT_MEMBER_FUNCTION_P (f)
+#else
+ !DECL_NONSTATIC_MEMBER_FUNCTION_P (f)
+#endif
+ )
+ continue;
+
+ if ((k == "get"
+ ? check_accessor (m, f, n, ma, false)
+ : check_modifier (m, f, n, ma, false)) == found_best)
+ break;
+ }
+
+ if (e.empty ())
+ {
+ error (ma.loc) << "unable to find suitable " << kind
+ << " function '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+ break;
+ }
+ default:
+ {
+ error (ma.loc) << "name '" << n << "' does not refer to a data "
+ << "member or function" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ // If there is no 'this' keyword, then add it as a prefix.
+ //
+ {
+ bool t (false);
+ for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ if (i->type == CPP_KEYWORD && i->literal == "this")
+ {
+ t = true;
+ break;
+ }
+ }
+
+ if (!t)
+ {
+ e.insert (e.begin (), cxx_token (0, CPP_DOT));
+ e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this"));
+ }
+ }
+
+ // Check that there is no placeholder in the accessor expression.
+ //
+ if (k == "get" && ma.placeholder ())
+ {
+ error (ma.loc) << "(?) placeholder in the accessor expression"
+ << endl;
+ throw operation_failed ();
+ }
+
+ // Check that the member type is default-constructible if we
+ // have a by value modifier.
+ //
+ if (k == "set" && ma.placeholder ())
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&utype (m)));
+
+ // Assume all other types are default-constructible.
+ //
+ if (c != 0)
+ {
+ // If this type is a class template instantiation, then make
+ // sure it is instantiated. While types used in real members
+ // will be instantiated, this is not necessarily the case for
+ // virtual members. Without the instantiation we won't be able
+ // to detect whether the type has the default ctor.
+ //
+ // It would have been cleaner to do it in post_process_pragmas()
+ // but there we don't yet know whether we need the default ctor.
+ // And it is a good idea not to require instantiability unless
+ // we really need it.
+ //
+ tree type (c->tree_node ());
+
+ if (!COMPLETE_TYPE_P (type) &&
+ CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+ {
+ // Reset input location so that we get nice diagnostics in
+ // case of an error. Use the location of the virtual pragma.
+ //
+ location_t loc (m.get<location_t> ("virtual-location"));
+ input_location = loc;
+
+ if (instantiate_class_template (type) == error_mark_node ||
+ errorcount != 0 ||
+ !COMPLETE_TYPE_P (type))
+ {
+ error (loc) << "unable to instantiate virtual data member " <<
+ "type" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (!c->default_ctor ())
+ {
+ error (ma.loc) << "modifier expression requires member type " <<
+ "to be default-constructible" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+
+ //
+ // Process section.
+ //
+
+ user_section&
+ process_user_section (semantics::data_member& m, semantics::class_& c)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ user_section::load_type l (
+ m.get ("section-load", user_section::load_eager));
+
+ user_section::update_type u (
+ m.get ("section-update", user_section::update_always));
+
+ if (l == user_section::load_eager && u == user_section::update_always)
+ {
+ location const& l (m.location ());
+
+ error (l) << "eager-loaded, always-updated section is pointless"
+ << endl;
+
+ info (l) << "use '#pragma db load' and/or '#pragma db update' to "
+ "specify an alternative loading and/or updating strategy" << endl;
+
+ info (l) << "or remove the section altogether" << endl;
+
+ throw operation_failed ();
+ }
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version));
+ user_section us (m, c, n, l, u);
+
+ // We may already have seen this section (e.g., forward reference
+ // from a member of this section).
+ //
+ user_sections::iterator i (find (uss.begin (), uss.end (), us));
+
+ if (i != uss.end ())
+ return *i;
+
+ // If we are adding a new section to an optimistic class with
+ // version in a base, make sure the base is sectionable.
+ //
+ semantics::data_member* opt (optimistic (c));
+ if (opt != 0 && &opt->scope () != &c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ semantics::node* base (poly_root ? poly_root : &opt->scope ());
+
+ if (!base->count ("sectionable"))
+ {
+ error (m.location ()) << "adding new section to a derived class " <<
+ "in an optimistic hierarchy requires sectionable base class" <<
+ endl;
+
+ info (base->location ()) << "use '#pragma db object sectionable' " <<
+ "to make the base class of this hierarchy sectionable" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ uss.push_back (us);
+ return uss.back ();
+ }
+
+ void
+ process_section_member (semantics::data_member& m)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ string name (m.get<string> ("section-member"));
+ location_t loc (m.get<location_t> ("section-member-location"));
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ try
+ {
+ data_member& us (c.lookup<data_member> (name, class_::include_hidden));
+
+ // Make sure we are referencing a section.
+ //
+ if (utype (us).fq_name () != "::odb::section")
+ {
+ error (loc) << "data member '" << name << "' in '#pragma db " <<
+ "section' is not of the odb::section type" << endl;
+ throw operation_failed ();
+ }
+
+ // If the section is in the base, handle polymorphic inheritance.
+ //
+ class_& b (dynamic_cast<class_&> (us.scope ()));
+ user_section* s (0);
+
+ if (&c != &b && poly_derived)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // This is a section override. See if we have already handled
+ // this section.
+ //
+ for (user_sections::iterator i (uss.begin ());
+ s == 0 && i != uss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ s = &*i;
+ }
+
+ // Otherwise, find and copy the nearest override in the base.
+ // The result should be a chain of overrides leading all the
+ // way to the original section.
+ //
+ if (s == 0)
+ {
+ for (class_* b (&polymorphic_base (c));;
+ b = &polymorphic_base (*b))
+ {
+ user_sections& buss (b->get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (buss.begin ());
+ s == 0 && i != buss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ {
+ uss.push_back (*i);
+ uss.back ().object = &c;
+ uss.back ().base = &*i;
+ s = &uss.back ();
+ }
+ }
+
+ if (s != 0)
+ break;
+
+ assert (b != poly_root); // We should have found it by now.
+ }
+ }
+ }
+ else
+ s = &process_user_section (us, c);
+
+ // Mark the member as added/deleted if the section is added/deleted.
+ // Also check that the version ordering is correct.
+ //
+ if (unsigned long long sav = added (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("added-location"));
+
+ if (unsigned long long mav = added (m))
+ {
+ location_t ml (m.get<location_t> ("added-location"));
+
+ if (mav < sav)
+ {
+ error (ml) << "member addition version is less than the " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mav == sav)
+ {
+ error (ml) << "member addition version is the same as " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("added", sav);
+ m.set ("added-location", sl);
+ }
+ }
+
+ if (unsigned long long sdv = deleted (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("deleted-location"));
+
+ if (unsigned long long mdv = deleted (m))
+ {
+ location_t ml (m.get<location_t> ("deleted-location"));
+
+ if (mdv > sdv)
+ {
+ error (ml) << "member deletion version is greater than the " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mdv == sdv)
+ {
+ error (ml) << "member deletion version is the same as " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("deleted", sdv);
+ m.set ("deleted-location", sl);
+ }
+ }
+
+ // Insert as object_section.
+ //
+ m.set ("section", static_cast<object_section*> (s));
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (loc) << "name '" << name << "' in '#pragma db section' " <<
+ "does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << name << "' " <<
+ "specified with '#pragma db section'" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << name << "' specified " <<
+ "with '#pragma db section' is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to " <<
+ "this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ //
+ // Process wrapper.
+ //
+
+ bool
+ process_wrapper (semantics::type& t)
+ {
+ if (t.count ("wrapper"))
+ return t.get<bool> ("wrapper");
+
+ // Check this type with wrapper_traits.
+ //
+ tree inst (instantiate_template (wrapper_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Get the wrapped type.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("wrapped_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ // The wrapped_type alias is a typedef in an instantiation
+ // that we just instantiated dynamically. As a result there
+ // is no semantic graph edges corresponding to this typedef
+ // since we haven't parsed it yet (unless it was instantiated
+ // explicitly by the user; see below). So to get the tree node
+ // that can actually be resolved to the graph node, we use
+ // the source type of this typedef.
+ //
+ tree type (DECL_ORIGINAL_TYPE (decl));
+
+ bool qc (CP_TYPE_CONST_P (type));
+ bool qv (CP_TYPE_VOLATILE_P (type));
+ bool qr (CP_TYPE_RESTRICT_P (type));
+
+ type = TYPE_MAIN_VARIANT (type);
+ semantics::type* wt (
+ dynamic_cast<semantics::type*> (unit.find (type)));
+
+ // Object pointers and wrappers often use the same smart
+ // pointers so check if the wrapped type is an object.
+ //
+ if (object (*wt))
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ if (qc || qv || qr)
+ {
+ for (semantics::type::qualified_iterator i (wt->qualified_begin ());
+ i != wt->qualified_end (); ++i)
+ {
+ semantics::qualifier& q (i->qualifier ());
+
+ if (q.const_ () == qc &&
+ q.volatile_ () == qv &&
+ q.restrict_ () == qr)
+ {
+ wt = &q;
+ break;
+ }
+ }
+ }
+
+ // Find the hint.
+ //
+ // If we can't find any, then try to fallback to the wrapped_type
+ // alias inside wrapper_traits. This requires an explicit
+ // wrapper_traits instantiation (see above).
+ //
+ semantics::names* wh (find_hint (unit, decl));
+
+ if (wh == nullptr)
+ wh = unit.find_hint (TREE_TYPE (decl));
+
+ t.set ("wrapper-type", wt);
+ t.set ("wrapper-hint", wh);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "wrapped_type type" << endl;
+ throw;
+ }
+
+ // Get the null_handler flag.
+ //
+ bool null_handler (false);
+
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_handler"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ null_handler = static_cast<bool> (integer_value (init));
+ t.set ("wrapper-null-handler", null_handler);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_handler constant" << endl;
+ throw;
+ }
+
+ // Get the null_default flag.
+ //
+ if (null_handler)
+ {
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_default"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("wrapper-null-default",
+ static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_default constant" << endl;
+ throw;
+ }
+ }
+
+ // Check if the wrapper is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+
+ t.set ("wrapper", true);
+ return true;
+ }
+
+ //
+ // Process object pointer.
+ //
+
+ semantics::class_*
+ process_object_pointer (semantics::data_member& m,
+ semantics::type& t,
+ string const& kp = string ())
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* c (0);
+
+ // The overall idea is as follows: try to instantiate the pointer
+ // traits class template. If we are successeful, then get the
+ // element type and see if it is an object.
+ //
+ if (t.count ("element-type"))
+ c = t.get<class_*> ("element-type");
+ else
+ {
+ tree inst (instantiate_template (pointer_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return 0;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string fl (DECL_SOURCE_FILE (decl));
+ size_t ln (DECL_SOURCE_LINE (decl));
+ size_t cl (DECL_SOURCE_COLUMN (decl));
+
+ // Get the element type.
+ //
+ tree tn (0);
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("element_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ tn = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
+
+ // Check if the pointer is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'element_type' type"
+ << endl;
+ throw;
+ }
+
+ c = dynamic_cast<class_*> (unit.find (tn));
+
+ if (c == 0 || !object (*c))
+ return 0;
+
+ t.set ("element-type", c);
+
+ // Determine the pointer kind.
+ //
+ try
+ {
+ tree kind (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (kind == error_mark_node || TREE_CODE (kind) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (kind) &&
+ !DECL_TEMPLATE_INSTANTIATED (kind) &&
+ !DECL_EXPLICIT_INSTANTIATION (kind))
+ instantiate_decl (kind, false, false);
+
+ tree init (DECL_INITIAL (kind));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ pointer_kind_type pk = static_cast<pointer_kind_type> (
+ integer_value (init));
+ t.set ("pointer-kind", pk);
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+
+ // Get the lazy flag.
+ //
+ try
+ {
+ tree lazy (
+ lookup_qualified_name (
+ inst, get_identifier ("lazy"), false, false));
+
+ if (lazy == error_mark_node || TREE_CODE (lazy) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (lazy) &&
+ !DECL_TEMPLATE_INSTANTIATED (lazy) &&
+ !DECL_EXPLICIT_INSTANTIATION (lazy))
+ instantiate_decl (lazy, false, false);
+
+ tree init (DECL_INITIAL (lazy));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("pointer-lazy", static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+ }
+
+ // See if this is the inverse side of a bidirectional relationship.
+ // If so, then resolve the member path and cache it in the context.
+ //
+ if (m.count ("inverse"))
+ {
+ string name (m.get<string> ("inverse"));
+ location_t l (m.get<location_t> ("inverse-location"));
+ data_member_path mp (resolve_data_members (*c, name, l, lex_));
+
+ {
+ string tl;
+ data_member& m (*mp.back ());
+
+ if (container (m) && lex_.next (tl) != CPP_EOF)
+ {
+ error (l) << "unexpect name after container member " <<
+ m.name () << " in '#pragma db inverse'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // Validate each member.
+ //
+ for (data_member_path::iterator i (mp.begin ()); i != mp.end (); ++i)
+ {
+ data_member& im (**i);
+ const string& n (im.name ());
+
+ if (im.count ("transient"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is transient" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (im.count ("inverse") || im.count ("value-inverse"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is itself inverse" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // @@ Would be good to check that the other end is actually
+ // an object pointer/points_to and points to the correct
+ // object. But the other class may not have been processed
+ // yet. Need to do in validator, pass 2.
+ //
+ m.remove ("inverse");
+ m.set (kp + (kp.empty () ? "": "-") + "inverse", mp);
+ }
+
+ return c;
+ }
+
+ //
+ // Process points-to pragma.
+ //
+
+ void
+ process_points_to (semantics::data_member& m,
+ string const& /*kp*/ = string ())
+ {
+ if (!m.count ("points-to"))
+ return;
+
+ using semantics::class_;
+
+ tree t (m.get<tree> ("points-to"));
+ location_t l (m.get<location_t> ("points-to-location"));
+
+ class_* c (dynamic_cast<class_*> (unit.find (t)));
+
+ if (c == 0 || !object (*c))
+ {
+ error (l) << "name specified with '#pragma db points_to' does "
+ << "not refer to an object" << endl;
+ throw operation_failed ();
+ }
+
+ m.remove ("points-to");
+ m.set (/*kp + (kp.empty () ? "": "-") + */"points-to", c);
+ }
+
+ //
+ // Process container.
+ //
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ if (obj_ptr)
+ process_object_pointer (m, t, prefix);
+ }
+
+ bool
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ // The overall idea is as follows: try to instantiate the container
+ // traits class template. If we are successeful, then this is a
+ // container type and we can extract the various information from
+ // the instantiation. Otherwise, this is not a container.
+ //
+ location ml (m.location ());
+
+ container_kind_type ck;
+ bool smart;
+ semantics::type* vt (0);
+ semantics::type* it (0);
+ semantics::type* kt (0);
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ if (t.count ("container-kind"))
+ {
+ ck = t.get<container_kind_type> ("container-kind");
+ smart = t.get<bool> ("container-smart");
+
+ vt = &utype (m, vh, "value");
+
+ if (ck == ck_ordered)
+ it = &utype (m, ih, "index");
+
+ if (ck == ck_map || ck == ck_multimap)
+ kt = &utype (m, kh, "key");
+ }
+ else
+ {
+ tree inst (instantiate_template (container_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return false;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Determine the container kind.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ ck = static_cast<container_kind_type> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "container kind constant" << endl;
+
+ throw;
+ }
+
+ t.set ("container-kind", ck);
+
+ // See if it is a smart container.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("smart"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ smart = static_cast<bool> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "'smart' constant" << endl;
+ throw;
+ }
+
+ // For now we only support ordered smart containers.
+ //
+ if (smart && ck != ck_ordered)
+ {
+ os << f << ":" << l << ":" << c << ": error: only ordered smart " <<
+ "containers are currently supported" << endl;
+ throw operation_failed ();
+ }
+
+ t.set ("container-smart", smart);
+
+ // Mark id column as not null.
+ //
+ t.set ("id-not-null", true);
+
+ // Get the value type.
+ //
+ {
+ tree decl (lookup_qualified_name (
+ inst, get_identifier ("value_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "value_type type" << endl;
+
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ vt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container value type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ vh = find_hint (unit, decl);
+ }
+
+
+ t.set ("value-tree-type", vt);
+ t.set ("value-tree-hint", vh);
+ vt = &utype (m, vh, "value"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("value-null") && vt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container value declared null while its type "
+ << "is declared not null" << endl;
+ }
+
+ // Get the index type for ordered containers.
+ //
+ if (ck == ck_ordered)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("index_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "index_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ it = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container index type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ ih = find_hint (unit, decl);
+
+ t.set ("index-not-null", true);
+ t.set ("index-tree-type", it);
+ t.set ("index-tree-hint", ih);
+ it = &utype (m, ih, "index"); // Map.
+ }
+
+ // Get the key type for maps.
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("key_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "key_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ kt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container key type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ kh = find_hint (unit, decl);
+
+ t.set ("key-tree-type", kt);
+ t.set ("key-tree-hint", kh);
+ kt = &utype (m, kh, "key"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("key-null") && kt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container key declared null while its type "
+ << "is declared not null" << endl;
+ }
+ }
+
+ // Determine if container value/index/key types are wrappers.
+ //
+ process_wrapper (*vt);
+
+ if (it != 0)
+ process_wrapper (*it);
+
+ if (kt != 0)
+ process_wrapper (*kt);
+
+ // Check if we are versioned. For now we are not allowing for
+ // soft-add/delete in container keys (might be used in WHERE,
+ // primary key).
+ //
+ {
+ semantics::class_* comp (0);
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ comp = composite_wrapper (*kt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+
+ error (ml) << "map key type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (kt->location ()) << "key type is defined here" << endl;
+ throw operation_failed ();
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ comp = composite_wrapper (*vt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = 0;
+ break;
+ }
+
+ error (ml) << "set value type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (vt->location ()) << "value type is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (force_versioned || (comp != 0 && column_count (*comp).soft != 0))
+ t.set ("versioned", true);
+ }
+ }
+
+ // Process member data.
+ //
+ m.set ("id-tree-type", &id_tree_type);
+
+ // Has to be first to handle inverse.
+ //
+ process_container_value (*vt, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, m, "key", true);
+
+ // A map cannot be an inverse container.
+ //
+ if (m.count ("value-inverse") && (ck == ck_map || ck == ck_multimap))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse container cannot be a map" << endl;
+ throw operation_failed ();
+ }
+
+ // If this is an inverse side of a bidirectional object relationship
+ // and it is an ordered container, mark it as unordred since there is
+ // no concept of order in this construct.
+ //
+ if (ck == ck_ordered && m.count ("value-inverse"))
+ m.set ("unordered", true);
+
+ // Issue an error if we have a non-inverse smart unordered container.
+ //
+ if (smart && ck == ck_ordered && unordered (m) &&
+ !m.count ("value-inverse"))
+ {
+ error (ml) << "smart ordered container cannot be unordered" << endl;
+ throw operation_failed ();
+ }
+
+ // Issue a warning if we are relaxing null-ness in the member.
+ //
+ if (m.count ("value-null") &&
+ (t.count ("value-not-null") || vt->count ("not-null")))
+ {
+ warn (ml) << "container value declared null while the container "
+ << "type or value type declares it as not null" << endl;
+ }
+
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ if (m.count ("key-null") &&
+ (t.count ("key-not-null") || kt->count ("not-null")))
+ {
+ warn (ml) << "container key declared null while the container "
+ << "type or key type declares it as not null" << endl;
+ }
+ }
+
+ return true;
+ }
+
+ //
+ // Implementation details (c-tor, helpers).
+ //
+
+ data_member ()
+ {
+ // Find the odb namespace.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find wrapper traits.
+ //
+ wrapper_traits_ = lookup_qualified_name (
+ odb, get_identifier ("wrapper_traits"), true, false);
+
+ if (wrapper_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (wrapper_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve wrapper_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find pointer traits.
+ //
+ pointer_traits_ = lookup_qualified_name (
+ odb, get_identifier ("pointer_traits"), true, false);
+
+ if (pointer_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (pointer_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve pointer_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find the access class.
+ //
+ tree access = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ if (access == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve access class"
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ access = TREE_TYPE (access);
+
+ // Find container_traits.
+ //
+ container_traits_ = lookup_qualified_name (
+ access, get_identifier ("container_traits"), true, false);
+
+ if (container_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (container_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve container_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ static tree
+ instantiate_template (tree t, tree arg)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = arg;
+
+ // This step should succeed regardles of whether there is a
+ // specialization for this type.
+ //
+ tree inst (
+ lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error));
+
+ if (inst == error_mark_node)
+ {
+ // Diagnostics has already been issued by lookup_template_class.
+ //
+ throw operation_failed ();
+ }
+
+ inst = TYPE_MAIN_VARIANT (inst);
+
+ // The instantiation may already be complete if it matches a
+ // (complete) specialization or was used before.
+ //
+ if (!COMPLETE_TYPE_P (inst))
+ inst = instantiate_class_template (inst);
+
+ // If we cannot instantiate this type, assume there is no suitable
+ // specialization for it.
+ //
+ if (inst == error_mark_node || !COMPLETE_TYPE_P (inst))
+ return 0;
+
+ return inst;
+ }
+
+ private:
+ tree wrapper_traits_;
+ tree pointer_traits_;
+ tree container_traits_;
+
+ cxx_string_lexer lex_;
+ };
+
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map")) {}
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ location const& l (m.location ());
+
+ if (lazy_pointer (t))
+ {
+ error (l) << "lazy object pointer in view" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the corresponding associated object. First see if this
+ // data member name matches any aliases.
+ //
+ view_alias_map::iterator i (amap_.find (m.name ()));
+
+ if (i == amap_.end ())
+ i = amap_.find (public_name (m, false));
+
+ view_object* vo (0);
+
+ if (i != amap_.end ())
+ {
+ vo = i->second;
+
+ if (vo->obj != c) // @@ Poly base/derived difference?
+ {
+ error (l) << "different pointed-to and associated objects" << endl;
+ info (vo->loc) << "associated object is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ // If there is no alias match, try the object type.
+ //
+ view_object_map::iterator i (omap_.find (c));
+
+ if (i == omap_.end ())
+ {
+ error (l) << "unable to find associated object for object "
+ << "pointer" << endl;
+ info (l) << "use associated object alias as this data member "
+ << "name" << endl;
+ throw operation_failed ();
+ }
+
+ vo = i->second;
+ }
+
+ if (vo->ptr != 0)
+ {
+ location const& l2 (vo->ptr->location ());
+
+ error (l) << "associated object is already loaded via another "
+ << "object pointer" << endl;
+ info (l2) << "the other data member is defined here" << endl;
+ info (l2) << "use associated object alias as this data member "
+ << "name to load a different object" << endl;
+
+ throw operation_failed ();
+ }
+
+ vo->ptr = &m;
+ m.set ("view-object", vo);
+ }
+ }
+
+ private:
+ semantics::class_& view_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ };
+
+ // Figure out the "summary" added/deleted version for a composite
+ // value type.
+ //
+ struct summary_version: object_members_base
+ {
+ summary_version (): av (0), dv (0), a_ (true), d_ (true) {}
+
+ virtual void
+ traverse_simple (semantics::data_member&)
+ {
+ if (a_)
+ {
+ if (unsigned long long v = added (member_path_))
+ {
+ if (av == 0 || av < v)
+ av = v;
+ }
+ else
+ {
+ av = 0;
+ a_ = false;
+ }
+ }
+
+ if (d_)
+ {
+ if (unsigned long long v = deleted (member_path_))
+ {
+ if (dv == 0 || dv > v)
+ dv = v;
+ }
+ else
+ {
+ dv = 0;
+ d_ = false;
+ }
+ }
+ }
+
+ public:
+ unsigned long long av;
+ unsigned long long dv;
+
+ bool a_;
+ bool d_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true),
+ std_string_ (0),
+ std_string_hint_ (0),
+ access_ (0)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+
+ // Resolve the std::string type node.
+ //
+ using semantics::scope;
+
+ for (scope::names_iterator_pair ip (unit.find ("std"));
+ ip.first != ip.second; ++ip.first)
+ {
+ if (scope* ns = dynamic_cast<scope*> (&ip.first->named ()))
+ {
+ scope::names_iterator_pair jp (ns->find ("string"));
+
+ if (jp.first != jp.second)
+ {
+ std_string_ = dynamic_cast<semantics::type*> (
+ &jp.first->named ());
+ std_string_hint_ = &*jp.first;
+ break;
+ }
+ }
+ }
+
+ assert (std_string_ != 0); // No std::string?
+
+ // Resolve odb::access, if any.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb != error_mark_node)
+ {
+ access_ = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+
+ // Check if odb::access is a friend of this class.
+ //
+ c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_));
+
+ // Assign pointer.
+ //
+ if (k == class_object || k == class_view)
+ assign_pointer (c);
+
+ if (k == class_object)
+ traverse_object_pre (c);
+ else if (k == class_view)
+ traverse_view_pre (c);
+
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object_post (c);
+ else if (k == class_view)
+ traverse_view_post (c);
+ else if (k == class_composite)
+ traverse_composite_post (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object_pre (type& c)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* poly_root (polymorphic (c));
+
+ // Sections.
+ //
+ user_sections& uss (c.set ("user-sections", user_sections (c)));
+
+ // Copy sections from reuse bases. For polymorphic classes, sections
+ // are overridden.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ user_sections& buss (b.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator j (buss.begin ());
+ j != buss.end ();
+ ++j)
+ {
+ // Don't copy the special version update section.
+ //
+ if (j->special == user_section::special_version)
+ continue;
+
+ uss.push_back (*j);
+ uss.back ().object = &c;
+ uss.back ().base = &*j;
+ }
+ }
+ }
+ }
+
+ // Determine whether it is a session object.
+ //
+ if (!c.count ("session"))
+ {
+ // If this is a derived class in a polymorphic hierarchy,
+ // then it should have the same session value as the root.
+ //
+ if (poly_root != 0 && poly_root != &c)
+ c.set ("session", session (*poly_root));
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // the session value.
+ //
+ bool found (false);
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (ns->count ("session"))
+ {
+ c.set ("session", ns->get<bool> ("session"));
+ found = true;
+ break;
+ }
+
+ if (ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // If still not found, then use the default value.
+ //
+ if (!found)
+ c.set ("session", options.generate_session ());
+ }
+ }
+
+ if (session (c))
+ features.session_object = true;
+
+ if (poly_root != 0)
+ {
+ using namespace semantics;
+ using semantics::data_member;
+
+ data_member_path& id (*id_member (*poly_root));
+ data_member* idm (id.front ());
+
+ if (poly_root != &c)
+ {
+ // If we are a derived class in the polymorphic persistent
+ // class hierarchy, then add a synthesized virtual pointer
+ // member that points back to the root.
+ //
+ semantics::class_& base (polymorphic_base (c));
+
+ if (&base != poly_root)
+ idm = &dynamic_cast<data_member&> (base.names_begin ()->named ());
+
+ path const& f (idm->file ());
+ size_t l (idm->line ()), col (idm->column ());
+
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (f, l, col, tree (0)));
+ m.set ("virtual", true);
+
+ // Make it the first member in the class. This is important:
+ // we rely on the corrensponding foreign key to be first.
+ //
+ node_position<type, scope::names_iterator> np (c, c.names_end ());
+ unit.new_edge<semantics::names> (
+ np, m, idm->name (), access::public_);
+
+ // Use the raw pointer as this member's type.
+ //
+ if (!base.pointed_p ())
+ {
+ // Create the pointer type in the graph. The pointer node
+ // in GCC seems to always be present, even if not explicitly
+ // used in the translation unit.
+ //
+ tree t (base.tree_node ());
+ tree ptr (TYPE_POINTER_TO (t));
+ assert (ptr != 0);
+ ptr = TYPE_MAIN_VARIANT (ptr);
+ pointer& p (unit.new_node<pointer> (f, l, col, ptr));
+ unit.insert (ptr, p);
+ unit.new_edge<points> (p, base);
+ assert (base.pointed_p ());
+ }
+
+ unit.new_edge<belongs> (m, base.pointed ().pointer ());
+
+ // Mark it as a special kind of id.
+ //
+ m.set ("id", string ());
+ m.set ("polymorphic-ref", true);
+
+ // Make sure we also use the same column name as the root.
+ //
+ if (composite_wrapper (utype (id)))
+ m.set ("column", table_column (column_prefix (id, true).prefix));
+ else
+ m.set ("column", table_column (column_name (id)));
+ }
+ else
+ {
+ // If we are a root of the polymorphic persistent class hierarchy,
+ // then add a synthesized virtual member for the discriminator.
+ // Use the location of the polymorphic pragma as the location of
+ // this member.
+ //
+ location_t loc (c.get<location_t> ("polymorphic-location"));
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (
+ path (LOCATION_FILE (loc)),
+ LOCATION_LINE (loc),
+ LOCATION_COLUMN (loc),
+ tree (0)));
+ m.set ("virtual", true);
+
+ // Insert it after the id member (or first if this id comes
+ // from reuse-base).
+ //
+ node_position<type, scope::names_iterator> np (
+ c, c.find (idm->named ()));
+ unit.new_edge<semantics::names> (
+ np, m, "typeid_", access::public_);
+
+ belongs& edge (unit.new_edge<belongs> (m, *std_string_));
+ edge.hint (*std_string_hint_);
+
+ m.set ("readonly", true);
+ m.set ("discriminator", true);
+
+ c.set ("discriminator", &m);
+ }
+ }
+ }
+
+ virtual void
+ traverse_object_post (type& c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ semantics::data_member* opt (optimistic (c));
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned ||
+ column_count (c).soft != 0 ||
+ (poly_derived && polymorphic_base (c).count ("versioned")))
+ c.set ("versioned", true);
+
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // See if we need to add a special fake section for version update.
+ //
+ if (c.count ("sectionable"))
+ {
+ uss.push_back (
+ user_section (*opt,
+ c,
+ uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version),
+ user_section::load_lazy,
+ user_section::update_manual,
+ user_section::special_version));
+
+ // If we are a root of a polymorphic hierarchy and the version is in
+ // a reuse-base, then we need to make sure that base is sectionable
+ // and derive from its special version update section.
+ //
+ semantics::node& opt_base (opt->scope ());
+ if (poly_root == &c && &opt_base != &c)
+ {
+ if (!opt_base.count ("sectionable"))
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+
+ error (l) << "reuse base class of a sectionable polymorphic " <<
+ "root class must be sectionable" << endl;
+
+ info (opt_base.location ()) << "use '#pragma db object " <<
+ "sectionable' to make the base class of this hierarchy " <<
+ "sectionable" << endl;
+
+ throw operation_failed ();
+ }
+
+ uss.back ().base =
+ &opt_base.get<user_sections> ("user-sections").back ();
+ }
+ }
+
+ // Calculate column counts for sections.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ column_count_type cc (column_count (c, &*i));
+ i->total = cc.total;
+ i->inverse = cc.inverse;
+ i->readonly = cc.readonly;
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned || cc.soft != 0 ||
+ (poly_derived && i->base != 0 && i->base->versioned))
+ i->versioned = true;
+
+ if (size_t n = has_a (c, test_container, &*i))
+ {
+ i->containers = true;
+ i->versioned_containers =
+ n != has_a (c,
+ test_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+
+ if ((n = has_a (c, test_readwrite_container, &*i)))
+ {
+ i->readwrite_containers = true;
+ i->readwrite_versioned_containers =
+ n != has_a (c,
+ test_readwrite_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+ }
+ }
+ }
+ }
+
+ //
+ // View.
+ //
+
+ virtual void
+ traverse_view_pre (type& c)
+ {
+ // Resolve referenced objects from tree nodes to semantic graph
+ // nodes. Also populate maps and compute counts.
+ //
+ view_alias_map& amap (c.set ("alias-map", view_alias_map ()));
+ view_object_map& omap (c.set ("object-map", view_object_map ()));
+
+ size_t& obj_count (c.set ("object-count", size_t (0)));
+ size_t& tbl_count (c.set ("table-count", size_t (0)));
+
+ if (c.count ("objects"))
+ {
+ using semantics::class_;
+
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ {
+ tbl_count++;
+ continue;
+ }
+ else
+ obj_count++;
+
+ tree n (TYPE_MAIN_VARIANT (i->obj_node));
+
+ if (TREE_CODE (n) != RECORD_TYPE)
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a class" << endl;
+
+ throw operation_failed ();
+ }
+
+ class_& o (dynamic_cast<class_&> (*unit.find (n)));
+
+ if (!object (o))
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a persistent class" << endl;
+
+ info (o.location ()) << "class '" << i->obj_name << "' is " <<
+ "defined here" << endl;
+
+ throw operation_failed ();
+ }
+
+ i->obj = &o;
+ i->ptr = 0; // Nothing yet.
+
+ if (i->alias.empty ())
+ {
+ if (!omap.insert (view_object_map::value_type (&o, &*i)).second)
+ {
+ error (i->loc) << "persistent class '" << i->obj_name <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[&o]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Also add the bases of a polymorphic object.
+ //
+ class_* poly_root (polymorphic (o));
+
+ if (poly_root != 0 && poly_root != &o)
+ {
+ for (class_* b (&polymorphic_base (o));;
+ b = &polymorphic_base (*b))
+ {
+ if (!omap.insert (view_object_map::value_type (b, &*i)).second)
+ {
+ error (i->loc) << "base class '" << class_name (*b) <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[b]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ if (b == poly_root)
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!amap.insert (
+ view_alias_map::value_type (i->alias, &*i)).second)
+ {
+ error (i->loc) << "alias '" << i->alias << "' is used in " <<
+ "the view more than once" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_view_post (type& c)
+ {
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+
+ // Figure out if we are versioned. Forced versioning is handled
+ // in relational/processing.
+ //
+ if (column_count (c).soft != 0)
+ c.set ("versioned", true);
+ }
+
+ //
+ // Composite.
+ //
+
+ virtual void
+ traverse_composite_post (type& c)
+ {
+ // Figure out if we are versioned.
+ //
+ if (force_versioned || column_count (c).soft != 0)
+ {
+ c.set ("versioned", true);
+
+ // See if we are "summarily" added/deleted, that is, all the
+ // columns are added/deleted. Note: this does not include
+ // containers.
+ //
+ summary_version sv;
+ sv.traverse (c);
+
+ // Note: there are no locations.
+ //
+ if (sv.av != 0)
+ c.set ("added", sv.av);
+
+ if (sv.dv != 0)
+ c.set ("deleted", sv.dv);
+ }
+ }
+
+ //
+ // Assign object/view pointer.
+ //
+
+ void
+ assign_pointer (type& c)
+ {
+ location_t loc (0); // Pragma location, or 0 if not used.
+
+ try
+ {
+ bool raw;
+ string ptr;
+ string const& type (class_fq_name (c));
+
+ tree decl (0); // Resolved template node.
+ string decl_name; // User-provided template name.
+ tree resolve_scope (0); // Scope in which we resolve names.
+
+ class_pointer const* cp (0);
+ bool cp_template (false);
+
+ if (c.count ("pointer"))
+ {
+ cp = &c.get<class_pointer> ("pointer");
+ }
+ // If we are a derived type in polymorphic hierarchy, then use
+ // our root's pointer type by default.
+ //
+ else if (semantics::class_* r = polymorphic (c))
+ {
+ if (&c != r && r->count ("pointer-template"))
+ cp = r->get<class_pointer const*> ("pointer-template");
+ }
+
+ if (cp != 0)
+ {
+ string const& p (cp->name);
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ cp_template = true;
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ raw = true;
+ ptr = p;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ // Template-id.
+ //
+ raw = false; // Fair to assume not raw, though technically can be.
+ ptr = p;
+ decl_name.assign (p, 0, p.find ('<'));
+ }
+ else
+ {
+ // This is not a template-id. Resolve it and see if it is a
+ // template or a type.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TYPE_DECL)
+ {
+ raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE);
+ ptr = p;
+
+ // This can be a typedef'ed alias for a TR1 template-id.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl)))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+ }
+ else
+ decl = 0; // Not a template.
+ }
+ else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ cp_template = true;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a type or a template" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ }
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // a pointer.
+ //
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (!ns->count ("pointer"))
+ {
+ if (ns->global_scope ()) // Note: namespace always named.
+ break;
+ else
+ continue;
+ }
+
+ cp = &ns->get<class_pointer> ("pointer");
+ string const& p (cp->name);
+
+ // Namespace-specified pointer can only be '*' or are template.
+ //
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a raw pointer" << endl;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a template-id" << endl;
+ }
+ else
+ {
+ // Resolve this name and make sure it is a template.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a template" << endl;
+ }
+ }
+
+ if (ptr.empty ())
+ throw operation_failed ();
+
+ cp_template = true;
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ break;
+ }
+
+ // Use the default pointer.
+ //
+ if (ptr.empty ())
+ {
+ string const& p (options.default_pointer ());
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+
+ // Resolve scope is the scope of the class.
+ //
+ resolve_scope = class_scope (c).tree_node ();
+ }
+ }
+
+ // If this class is a root of a polymorphic hierarchy, then cache
+ // the pointer template so that we can use it for derived classes.
+ //
+ if (cp != 0 && cp_template && polymorphic (c) == &c)
+ c.set ("pointer-template", cp);
+
+ // Check if we are using TR1.
+ //
+ if (decl != 0 || !decl_name.empty ())
+ {
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ // First check the user-supplied name.
+ //
+ tr1 = tr1
+ || decl_name.compare (0, 8, "std::tr1") == 0
+ || decl_name.compare (0, 10, "::std::tr1") == 0;
+
+ // If there was no match, also resolve the name since it can be
+ // a using-declaration for a TR1 template.
+ //
+ if (!tr1)
+ {
+ if (decl == 0)
+ decl = resolve_name (decl_name, resolve_scope, false);
+
+ if (TREE_CODE (decl) != TEMPLATE_DECL || !
+ DECL_CLASS_TEMPLATE_P (decl))
+ {
+ // This is only checked for the --default-pointer option.
+ //
+ error (c.file (), c.line (), c.column ())
+ << "name '" << decl_name << "' specified with the "
+ << "--default-pointer option does not name a class "
+ << "template" << endl;
+
+ throw operation_failed ();
+ }
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+
+ // Fully-qualify all the unqualified components of the name.
+ //
+ try
+ {
+ lex_.start (ptr);
+ ptr.clear ();
+
+ string t;
+ bool punc (false);
+ bool scoped (false);
+
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ ptr += ' ';
+
+ punc = false;
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ ptr += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ ptr += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ ptr += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // If the name was not preceeded with '::', look it
+ // up in the pragmas's scope and add the qualifer.
+ //
+ if (!scoped)
+ {
+ tree decl (resolve_name (t, resolve_scope, false));
+ tree scope (CP_DECL_CONTEXT (decl));
+
+ // If this is an inline namespace, skip it until we get
+ // to the non-inline one.
+ //
+ while (scope != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (scope));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, scope, true))
+#else
+ if (!is_associated_namespace (prev, scope))
+#endif
+ break;
+
+ scope = prev;
+ }
+
+ if (scope != global_namespace)
+ {
+ ptr += "::";
+ ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER);
+ }
+
+ ptr += "::";
+ }
+
+ ptr += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ ptr += t;
+ punc = true;
+ break;
+ }
+ default:
+ {
+ ptr += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ }
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw operation_failed ();
+ }
+
+ c.set ("object-pointer", ptr);
+ c.set ("object-pointer-raw", raw);
+ }
+ catch (invalid_name const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "name '" << ex.name () << "' specified with db pragma "
+ << "pointer is invalid" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "name '" << ex.name () << "' specified with the "
+ << "--default-pointer option is invalid" << endl;
+
+
+ throw operation_failed ();
+ }
+ catch (unable_to_resolve const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with db pragma pointer" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with the --default-pointer option" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ struct invalid_name
+ {
+ invalid_name (string const& n): name_ (n) {}
+
+ string const&
+ name () const {return name_;}
+
+ private:
+ string name_;
+ };
+
+ typedef lookup::unable_to_resolve unable_to_resolve;
+
+ tree
+ resolve_name (string const& qn, tree scope, bool is_type)
+ {
+ try
+ {
+ string tl;
+ tree tn;
+ cpp_ttype tt, ptt;
+
+ nlex_.start (qn);
+ tt = nlex_.next (tl, &tn);
+
+ string name;
+ return lookup::resolve_scoped_name (
+ nlex_, tt, tl, tn, ptt, scope, name, is_type);
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw invalid_name (qn);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ throw invalid_name (qn);
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+
+ cxx_string_lexer lex_;
+ cxx_string_lexer nlex_; // Nested lexer.
+
+ semantics::type* std_string_;
+ semantics::names* std_string_hint_;
+
+ tree access_; // odb::access node.
+ };
+
+ static bool
+ check_to_from (const cxx_tokens& ex, const char* c, location_t l)
+ {
+ // Make sure we have one and only one placeholder (?).
+ //
+ bool r (false), m (true);
+
+ for (cxx_tokens::const_iterator i (ex.begin ()), e (ex.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ {
+ if (r)
+ m = false; // Multiple (?), can't move.
+ else
+ r = true;
+ }
+ }
+ }
+ else
+ ++i;
+ }
+
+ if (!r)
+ {
+ error (l) << "no '(?)' expression in the '" << c << "' clause "
+ << "of db pragma map" << endl;
+
+ throw operation_failed ();
+ }
+
+ return m;
+ }
+}
+
+static void
+process1 (semantics::unit& u)
+{
+ // Process custom C++ type mapping.
+ //
+
+ // Create an empty list if we don't have one. This makes the
+ // rest of the code simpler.
+ //
+ if (!u.count ("custom-cxx-types"))
+ u.set ("custom-cxx-types", custom_cxx_types ());
+
+ custom_cxx_types & cts (u.get<custom_cxx_types> ("custom-cxx-types"));
+
+ for (custom_cxx_types::iterator i (cts.begin ()); i != cts.end (); ++i)
+ {
+ custom_cxx_type& ct (*i);
+
+ // type
+ //
+ if (ct.type_node == 0)
+ {
+ error (ct.loc) << "'type' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.type = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.type_node)));
+ ct.type_hint = u.find_hint (ct.type_node);
+
+ // as
+ //
+ if (ct.as_node == 0)
+ {
+ error (ct.loc) << "'as' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.as = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.as_node)));
+ ct.as_hint = u.find_hint (ct.as_node);
+
+ // to
+ //
+ {
+ cxx_tokens& e (ct.to);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.to_move = true;
+ }
+ else
+ ct.to_move = check_to_from (e, "to", ct.loc);
+ }
+
+ // from
+ //
+ {
+ cxx_tokens& e (ct.from);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.from_move = true;
+ }
+ else
+ ct.from_move = check_to_from (e, "from", ct.loc);
+ }
+
+ // Resolve mapping scope.
+ //
+ semantics::scope* s (dynamic_cast<semantics::scope*> (u.find (ct.scope)));
+ if (s == 0)
+ {
+ error (ct.loc) << "unable to resolve db pragma map scope" << endl;
+ throw operation_failed ();
+ }
+
+ if (semantics::namespace_* ns = dynamic_cast<semantics::namespace_*> (s))
+ {
+ if (ns->extension ())
+ s = &ns->original ();
+ }
+
+ // Enter into the map.
+ //
+ if (!s->count ("custom-cxx-type-map"))
+ s->set ("custom-cxx-type-map", custom_cxx_type_map ());
+
+ s->get<custom_cxx_type_map> ("custom-cxx-type-map")[ct.type] = &ct;
+ }
+}
+
+static void
+process2 (options const& ops, features& f, semantics::unit& u)
+{
+ unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ // Common processing.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx->unit);
+ }
+
+ // Database-specific processing.
+ //
+ switch (ops.database ()[0])
+ {
+ case database::common:
+ {
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::process ();
+ break;
+ }
+ }
+}
+
+void
+process (options const& ops,
+ features& f,
+ semantics::unit& u,
+ semantics::path const&,
+ unsigned short pass)
+{
+ try
+ {
+ if (pass == 1)
+ process1 (u);
+ else if (pass == 2)
+ process2 (ops, f, u);
+ }
+ catch (operation_failed const&)
+ {
+ // Processing failed. Diagnostics has already been issued.
+ //
+ throw processor_failed ();
+ }
+}