// file      : odb/processor.cxx
// copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC
// 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
{
  // Indirect (dynamic) context values.
  //
  static semantics::type*
  id_tree_type ()
  {
    context& c (context::current ());
    semantics::data_member& id (*context::id_member (*c.top_object));
    return &id.type ();
  }

  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:
      //
      // auto_ptr<const T> - can modify by setting a new pointer
      // const auto_ptr<T> - can modify by changing the pointed-to value
      //
      if (const_type (m.type ()) &&
          !(id (m) || version (m) || m.count ("inverse")))
      {
        if (qwt == 0 || const_type (*qwt))
          m.set ("readonly", true);
      }

      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, n));
      e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n));

      // 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, n));
        e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n));

        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, n));
          e.push_back (cxx_token (0, CPP_QUERY));
          e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n));

          // 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.
            //
            for (tree o (BASELINK_FUNCTIONS (decl));
                 o != 0;
                 o = OVL_NEXT (o))
            {
              tree f (OVL_CURRENT (o));

              // We are only interested in public non-static member
              // functions. Note that TREE_PUBLIC() returns something
              // other than what we need.
              //
              if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) ||
                  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.
            //
            for (tree o (BASELINK_FUNCTIONS (decl));
                 o != 0;
                 o = OVL_NEXT (o))
            {
              tree f (OVL_CURRENT (o));

              // We are only interested in non-static member functions.
              //
              if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f))
                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. 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.
        //
        semantics::names* wh (0);

        for (tree ot (DECL_ORIGINAL_TYPE (decl));
             ot != 0;
             ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
        {
          if ((wh = unit.find_hint (ot)))
            break;

          decl = TYPE_NAME (ot);
        }

        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 ())
    {
      // 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.
      //
      using semantics::class_;
      using semantics::data_member;

      class_* c (0);

      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;
        }
      }

      bool poly (polymorphic (*c));
      bool abst (abstract (*c));

      // Make sure the pointed-to class is complete.
      //
      if (!c->complete ())
      {
        os << m.file () << ":" << m.line () << ":" << m.column () << ": "
           << "error: pointed-to class '" << class_fq_name (*c) << "' "
           << "is incomplete" << endl;

        os << c->file () << ":" << c->line () << ":" << c->column () << ": "
           << "info: class '" << class_name (*c) << "' is declared here"
           << endl;

        os << c->file () << ":" << c->line () << ":" << c->column () << ": "
           << "info: consider including its definition with the "
           << "--odb-epilogue option" << endl;

        throw operation_failed ();
      }

      // Make sure the pointed-to class is not reuse-abstract.
      //
      if (abst && !poly)
      {
        os << m.file () << ":" << m.line () << ":" << m.column () << ": "
           << "error: pointed-to class '" << class_fq_name (*c) << "' "
           << "is abstract" << endl;

        os << c->file () << ":" << c->line () << ":" << c->column () << ": "
           << "info: class '" << class_name (*c) << "' is defined here"
           << endl;

        throw operation_failed ();
      }

      // Make sure the pointed-to class has object id unless it is in a
      // view where we can load no-id objects.
      //
      if (id_member (*c) == 0 && !view_member (m))
      {
        os << m.file () << ":" << m.line () << ":" << m.column () << ": "
           << "error: pointed-to class '" << class_fq_name (*c) << "' "
           << "has no object id" << endl;

        os << c->file () << ":" << c->line () << ":" << c->column () << ": "
           << "info: class '" << class_name (*c) << "' is defined here"
           << endl;

        throw operation_failed ();
      }

      // Make sure the pointed-to class has a default ctor. Since we will
      // use database::load() in the generated code, lack of a default ctor
      // will lead to uncompilable generated code. Poly-abstract is Ok.
      //
      if (!c->default_ctor () && !(abst && poly))
      {
        os << m.file () << ":" << m.line () << ":" << m.column () << ": "
           << "error: pointed-to class '" << class_fq_name (*c) << "' "
           << "has no default constructor" << endl;

        os << c->file () << ":" << c->line () << ":" << c->column () << ": "
           << "info: class '" << class_name (*c) << "' is defined here"
           << endl;

        throw operation_failed ();
      }

      // See if this is the inverse side of a bidirectional relationship.
      // If so, then resolve the member and cache it in the context.
      //
      if (m.count ("inverse"))
      {
        string name (m.get<string> ("inverse"));
        location_t loc (m.get<location_t> ("inverse-location"));

        try
        {
          data_member& im (
            c->lookup<data_member> (name, class_::include_hidden));

          if (im.count ("transient"))
          {
            error (loc) << "data member '" << name << "' specified with " <<
              "'#pragma db inverse' is transient" << endl;
            info (im.location ()) << "data member '" << name << "' is " <<
              "defined here" << endl;
            throw operation_failed ();
          }

          if (im.count ("inverse") || im.count ("value-inverse"))
          {
            error (loc) << "data member '" << name << "' specified with " <<
              "'#pragma db inverse' is inverse" << endl;
            info (im.location ()) << "data member '" << name << "' is " <<
              "defined here" << endl;
            throw operation_failed ();
          }

          // @@ Would be good to check that the other end is actually
          // an object pointer 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", &im);
        }
        catch (semantics::unresolved const& e)
        {
          if (e.type_mismatch)
            error (loc) << "name '" << name << "' in '#pragma db " <<
              "inverse' does not refer to a data member" << endl;
          else
            error (loc) << "unable to resolve data member '" << name <<
              "' specified with '#pragma db inverse'" << endl;

          throw operation_failed ();
        }
        catch (semantics::ambiguous const& e)
        {
          error (loc) << "data member name '" << name << "' specified " <<
            "with '#pragma db inverse' 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 ();
        }
      }

      return c;
    }

    //
    // Process container.
    //

    void
    process_container_value (semantics::type& t,
                             semantics::data_member& m,
                             string const& prefix,
                             bool obj_ptr)
    {
      process_wrapper (t);

      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.
      //

      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 = t.get<semantics::type*> ("value-tree-type");
        vh = t.get<semantics::names*> ("value-tree-hint");

        if (ck == ck_ordered)
        {
          it = t.get<semantics::type*> ("index-tree-type");
          ih = t.get<semantics::names*> ("index-tree-hint");
        }

        if (ck == ck_map || ck == ck_multimap)
        {
          kt = t.get<semantics::type*> ("key-tree-type");
          kh = t.get<semantics::names*> ("key-tree-hint");
        }
      }
      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.
        //
        try
        {
          tree decl (
            lookup_qualified_name (
              inst, get_identifier ("value_type"), true, false));

          if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
            throw operation_failed ();

          tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
          vt = &dynamic_cast<semantics::type&> (*unit.find (type));

          // Find the hint.
          //
          for (tree ot (DECL_ORIGINAL_TYPE (decl));
               ot != 0;
               ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
          {
            if ((vh = unit.find_hint (ot)))
              break;

            decl = TYPE_NAME (ot);
          }
        }
        catch (operation_failed const&)
        {
          os << f << ":" << l << ":" << c << ": error: "
             << "container_traits specialization does not define the "
             << "value_type type" << endl;

          throw;
        }

        t.set ("value-tree-type", vt);
        t.set ("value-tree-hint", vh);

        // If we have a set container, automatically mark the value
        // column as not null. If we already have an explicit null for
        // this column, issue an error.
        //
        if (ck == ck_set)
        {
          if (t.count ("value-null"))
          {
            os << t.file () << ":" << t.line () << ":" << t.column () << ":"
               << " error: set container cannot contain null values" << endl;

            throw operation_failed ();
          }
          else
            t.set ("value-not-null", true);
        }

        // 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)
        {
          try
          {
            tree decl (
              lookup_qualified_name (
                inst, get_identifier ("index_type"), true, false));

            if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
              throw operation_failed ();

            tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
            it = &dynamic_cast<semantics::type&> (*unit.find (type));

            // Find the hint.
            //
            for (tree ot (DECL_ORIGINAL_TYPE (decl));
                 ot != 0;
                 ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
            {
              if ((ih = unit.find_hint (ot)))
                break;

              decl = TYPE_NAME (ot);
            }
          }
          catch (operation_failed const&)
          {
            os << f << ":" << l << ":" << c << ": error: "
               << "container_traits specialization does not define the "
               << "index_type type" << endl;

            throw;
          }

          t.set ("index-tree-type", it);
          t.set ("index-tree-hint", ih);
          t.set ("index-not-null", true);
        }

        // Get the key type for maps.
        //
        if (ck == ck_map || ck == ck_multimap)
        {
          try
          {
            tree decl (
              lookup_qualified_name (
                inst, get_identifier ("key_type"), true, false));

            if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
              throw operation_failed ();

            tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
            kt = &dynamic_cast<semantics::type&> (*unit.find (type));

            // Find the hint.
            //
            for (tree ot (DECL_ORIGINAL_TYPE (decl));
                 ot != 0;
                 ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
            {
              if ((kh = unit.find_hint (ot)))
                break;

              decl = TYPE_NAME (ot);
            }
          }
          catch (operation_failed const&)
          {
            os << f << ":" << l << ":" << c << ": error: "
               << "container_traits specialization does not define the "
               << "key_type type" << endl;

            throw;
          }

          t.set ("key-tree-type", kt);
          t.set ("key-tree-hint", kh);
          t.set ("key-not-null", true);
        }

        // 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 (*vt);
              break;
            }
          case ck_map:
          case ck_multimap:
            {
              comp = composite (*kt);
              if (comp == 0 || column_count (*comp).soft == 0)
              {
                comp = composite (*vt);
                break;
              }

              error (m.location ()) << "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 (*vt);
              if (comp == 0 || column_count (*comp).soft == 0)
              {
                comp = 0;
                break;
              }

              error (m.location ()) << "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", false);

      // 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"))
      {
        os << m.file () << ":" << m.line () << ":" << m.column () << ":"
           << " error: smart ordered container cannot be unordered" << endl;
        throw operation_failed ();
      }

      // Issue an error if we have a null column in a set container.
      // This can only happen if the value is declared as null in
      // the member.
      //
      if (ck == ck_set && m.count ("value-null"))
      {
        os << m.file () << ":" << m.line () << ":" << m.column () << ":"
           << " error: set container cannot contain null values" << 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")))
      {
        os << m.file () << ":" << m.line () << ":" << m.column () << ":"
           << " warning: container value declared null while the container "
           << "type or value 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_;
  };

  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)
    {
      semantics::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)
              continue; // Some other scope.

            if (ns->extension ())
              ns = &ns->original ();

            if (ns->count ("session"))
            {
              c.set ("session", ns->get<bool> ("session"));
              found = true;
              break;
            }

            if (ns->global_scope ())
              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* idm (id_member (*poly_root));

        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", true);
          m.set ("polymorphic-ref", true);
        }
        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)
              continue; // Some other scope.

            if (ns->extension ())
              ns = &ns->original ();

            if (!ns->count ("pointer"))
            {
              if (ns->global_scope ())
                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 (!is_associated_namespace (prev, scope))
                      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.
  };
}

void processor::
process (options const& ops,
         features& f,
         semantics::unit& unit,
         semantics::path const&)
{
  try
  {
    auto_ptr<context> ctx (create_context (cerr, unit, 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;
      }
    }
  }
  catch (operation_failed const&)
  {
    // Processing failed. Diagnostics has already been issued.
    //
    throw failed ();
  }
}
