//
// Copyright (c) ZeroC, Inc. All rights reserved.
//

#include <IceUtil/Functional.h>
#include <IceUtil/InputUtil.h>
#include <IceUtil/StringUtil.h>
#include <Slice/Parser.h>
#include <Slice/GrammarUtil.h>
#include <Slice/Util.h>
#include <cstring>
#include <iterator>

// TODO: fix this warning once we no longer support VS2013 and earlier
#if defined(_MSC_VER) && (_MSC_VER >= 1900)
#    pragma warning(disable:4589) // Constructor of abstract class 'Slice::Type' ignores initializer...
#endif

using namespace std;
using namespace Slice;

Slice::CompilerException::CompilerException(const char* file, int line, const string& r) :
    IceUtil::Exception(file, line),
    _reason(r)
{
}

#ifndef ICE_CPP11_COMPILER
Slice::CompilerException::~CompilerException() throw()
{
}
#endif

string
Slice::CompilerException::ice_id() const
{
    return "::Slice::CompilerException";
}

void
Slice::CompilerException::ice_print(ostream& out) const
{
    IceUtil::Exception::ice_print(out);
    out << ": " << _reason;
}

#ifndef ICE_CPP11_MAPPING
Slice::CompilerException*
Slice::CompilerException::ice_clone() const
{
    return new CompilerException(*this);
}
#endif

void
Slice::CompilerException::ice_throw() const
{
    throw *this;
}

string
Slice::CompilerException::reason() const
{
    return _reason;
}

extern FILE* slice_in;
extern int slice_debug;

int slice_parse();

//
// Operation attributes
//
// read + supports must be 0 (the default)
//

namespace
{

string readWriteAttribute[] = { "read", "write" };
string txAttribute[] = { "supports", "mandatory", "required", "never" };
enum { Supports, Mandatory, Required, Never };

DataMemberList
filterOrderedOptionalDataMembers(const DataMemberList& members)
{
    class SortFn
    {
    public:
        static bool compare(const DataMemberPtr& lhs, const DataMemberPtr& rhs)
        {
            return lhs->tag() < rhs->tag();
        }
    };

    DataMemberList result;
    for(DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p)
    {
        if((*p)->optional())
        {
            result.push_back(*p);
        }
    }
    result.sort(SortFn::compare);
    return result;
}

void
sortOptionalParameters(ParamDeclList& params)
{
    //
    // Sort optional parameters by tag.
    //
    class SortFn
    {
    public:
        static bool compare(const ParamDeclPtr& lhs, const ParamDeclPtr& rhs)
        {
            return lhs->tag() < rhs->tag();
        }
    };
    params.sort(SortFn::compare);
}

bool
isMutableAfterReturnType(const TypePtr& type)
{
    //
    // Returns true if the type contains data types which can be referenced by user code
    // and mutated after a dispatch returns.
    //

    if(ClassDeclPtr::dynamicCast(type))
    {
        return true;
    }

    BuiltinPtr builtin = BuiltinPtr::dynamicCast(type);
    if(builtin && (builtin->kind() == Builtin::KindObject || builtin->kind() == Builtin::KindValue))
    {
        return true;
    }

    if(SequencePtr::dynamicCast(type) || DictionaryPtr::dynamicCast(type))
    {
        return true;
    }

    StructPtr s = StructPtr::dynamicCast(type);
    if(s)
    {
        return true;
    }

    return false;
}

void
checkDeprecatedType(const UnitPtr& unt, const TypePtr& type)
{
    ClassDeclPtr decl = ClassDeclPtr::dynamicCast(type);
    if(decl && !decl->isLocal() && decl->isInterface())
    {
        unt->warning(Deprecated, "interface by value is deprecated");
    }

    ProxyPtr proxy = ProxyPtr::dynamicCast(type);
    if(proxy && !proxy->_class()->isInterface())
    {
        unt->warning(Deprecated, "proxy for a class is deprecated");
    }
}

}

namespace Slice
{

Unit* unit;

}

// ----------------------------------------------------------------------
// DefinitionContext
// ----------------------------------------------------------------------

Slice::DefinitionContext::DefinitionContext(int includeLevel, const StringList& metaData) :
    _includeLevel(includeLevel), _metaData(metaData), _seenDefinition(false)
{
    initSuppressedWarnings();
}

string
Slice::DefinitionContext::filename() const
{
    return _filename;
}

int
Slice::DefinitionContext::includeLevel() const
{
    return _includeLevel;
}

bool
Slice::DefinitionContext::seenDefinition() const
{
    return _seenDefinition;
}

void
Slice::DefinitionContext::setFilename(const string& filename)
{
    _filename = filename;
}

void
Slice::DefinitionContext::setSeenDefinition()
{
    _seenDefinition = true;
}

bool
Slice::DefinitionContext::hasMetaData() const
{
    return !_metaData.empty();
}

void
Slice::DefinitionContext::setMetaData(const StringList& metaData)
{
    _metaData = metaData;
    initSuppressedWarnings();
}

string
Slice::DefinitionContext::findMetaData(const string& prefix) const
{
    for(StringList::const_iterator p = _metaData.begin(); p != _metaData.end(); ++p)
    {
        if((*p).find(prefix) == 0)
        {
            return *p;
        }
    }

    return string();
}

StringList
Slice::DefinitionContext::getMetaData() const
{
    return _metaData;
}

void
Slice::DefinitionContext::warning(WarningCategory category, const string& file, int line, const string& msg) const
{
    if(!suppressWarning(category))
    {
        emitWarning(file, line, msg);
    }
}

void
Slice::DefinitionContext::warning(WarningCategory category, const string& file, const string& line, const string& msg) const
{
    if(!suppressWarning(category))
    {
        emitWarning(file, line, msg);
    }
}

void
Slice::DefinitionContext::error(const string& file, int line, const string& msg) const
{
    emitError(file, line, msg);
    throw CompilerException(__FILE__, __LINE__, msg);
}

void
Slice::DefinitionContext::error(const string& file, const string& line, const string& msg) const
{
    emitError(file, line, msg);
    throw CompilerException(__FILE__, __LINE__, msg);
}

bool
Slice::DefinitionContext::suppressWarning(WarningCategory category) const
{
    return _suppressedWarnings.find(category) != _suppressedWarnings.end() ||
        _suppressedWarnings.find(All) != _suppressedWarnings.end();
}

void
Slice::DefinitionContext::initSuppressedWarnings()
{
    _suppressedWarnings.clear();
    const string prefix = "suppress-warning";
    string value = findMetaData(prefix);
    if(value == prefix)
    {
        _suppressedWarnings.insert(All);
    }
    else if(!value.empty())
    {
        assert(value.length() > prefix.length());
        if(value[prefix.length()] == ':')
        {
            value = value.substr(prefix.length() + 1);
            vector<string> result;
            IceUtilInternal::splitString(value, ",", result);
            for(vector<string>::iterator p = result.begin(); p != result.end(); ++p)
            {
                string s = IceUtilInternal::trim(*p);
                if(s == "all")
                {
                    _suppressedWarnings.insert(All);
                }
                else if(s == "deprecated")
                {
                    _suppressedWarnings.insert(Deprecated);
                }
                else if(s == "invalid-metadata")
                {
                    _suppressedWarnings.insert(InvalidMetaData);
                }
                else
                {
                    warning(InvalidMetaData, "", "", string("invalid category `") + s +
                            "' in file metadata suppress-warning");
                }
            }
        }
    }
}

// ----------------------------------------------------------------------
// Comment
// ----------------------------------------------------------------------

bool
Slice::Comment::isDeprecated() const
{
    return _isDeprecated;
}

StringList
Slice::Comment::deprecated() const
{
    return _deprecated;
}

StringList
Slice::Comment::overview() const
{
    return _overview;
}

StringList
Slice::Comment::misc() const
{
    return _misc;
}

StringList
Slice::Comment::seeAlso() const
{
    return _seeAlso;
}

StringList
Slice::Comment::returns() const
{
    return _returns;
}

map<string, StringList>
Slice::Comment::parameters() const
{
    return _parameters;
}

map<string, StringList>
Slice::Comment::exceptions() const
{
    return _exceptions;
}

Slice::Comment::Comment()
{
}

// ----------------------------------------------------------------------
// SyntaxTreeBase
// ----------------------------------------------------------------------

void
Slice::SyntaxTreeBase::destroy()
{
    _unit = 0;
}

UnitPtr
Slice::SyntaxTreeBase::unit() const
{
    return _unit;
}

DefinitionContextPtr
Slice::SyntaxTreeBase::definitionContext() const
{
    return _definitionContext;
}

void
Slice::SyntaxTreeBase::visit(ParserVisitor*, bool)
{
}

Slice::SyntaxTreeBase::SyntaxTreeBase(const UnitPtr& unt, const DefinitionContextPtr& definitionContext) :
    _unit(unt),
    _definitionContext(definitionContext)
{
    if(!_definitionContext && _unit)
    {
        _definitionContext = unt->currentDefinitionContext();
    }
}

// ----------------------------------------------------------------------
// Type
// ----------------------------------------------------------------------

Slice::Type::Type(const UnitPtr& unt) :
    SyntaxTreeBase(unt)
{
}

// ----------------------------------------------------------------------
// Builtin
// ----------------------------------------------------------------------

bool
Slice::Builtin::isLocal() const
{
    return _kind == KindLocalObject;
}

string
Slice::Builtin::typeId() const
{
    switch(_kind)
    {
        case KindByte:
        {
            return "byte";
            break;
        }
        case KindBool:
        {
            return "bool";
            break;
        }
        case KindShort:
        {
            return "short";
            break;
        }
        case KindInt:
        {
            return "int";
            break;
        }
        case KindLong:
        {
            return "long";
            break;
        }
        case KindFloat:
        {
            return "float";
            break;
        }
        case KindDouble:
        {
            return "double";
            break;
        }
        case KindString:
        {
            return "string";
            break;
        }
        case KindObject:
        {
            return "::Ice::Object";
            break;
        }
        case KindObjectProxy:
        {
            return "::Ice::Object*";
            break;
        }
        case KindLocalObject:
        {
            return "::Ice::LocalObject";
            break;
        }
        case KindValue:
        {
            return "::Ice::Value";
            break;
        }
    }
    assert(false);
    return ""; // Keep the compiler happy.
}

bool
Slice::Builtin::usesClasses() const
{
    return _kind == KindObject || _kind == KindValue;
}

size_t
Slice::Builtin::minWireSize() const
{
    static size_t minWireSizeTable[] =
    {
        1, // KindByte
        1, // KindBool
        2, // KindShort
        4, // KindInt
        8, // KindLong
        4, // KindFloat
        8, // KindDouble
        1, // KindString: at least one byte for an empty string.
        1, // KindObject: at least one byte (to marshal an index instead of an instance).
        2, // KindObjectProxy: at least an empty identity for a nil proxy, that is, 2 bytes.
        0, // KindLocalObject: unused
        1  // KindValue: at least one byte (to marshal an index instead of an instance).
    };

    assert(_kind != KindLocalObject);
    return minWireSizeTable[_kind];
}

bool
Slice::Builtin::isVariableLength() const
{
    return _kind == KindString || _kind == KindObject || _kind == KindObjectProxy || _kind == KindValue;
}

Builtin::Kind
Slice::Builtin::kind() const
{
    return _kind;
}

string
Builtin::kindAsString() const
{
    return builtinTable[_kind];
}

const char* Slice::Builtin::builtinTable[] =
    {
        "byte",
        "bool",
        "short",
        "int",
        "long",
        "float",
        "double",
        "string",
        "Object",
        "Object*",
        "LocalObject",
        "Value"
    };

Slice::Builtin::Builtin(const UnitPtr& ut, Kind kind) :
    SyntaxTreeBase(ut),
    Type(ut),
    _kind(kind)
{
    //
    // Builtin types do not have a definition context.
    //
    _definitionContext = 0;
}

// ----------------------------------------------------------------------
// Contained
// ----------------------------------------------------------------------

ContainerPtr
Slice::Contained::container() const
{
    return _container;
}

string
Slice::Contained::name() const
{
    return _name;
}

string
Slice::Contained::scoped() const
{
    return _scoped;
}

string
Slice::Contained::scope() const
{
    string::size_type idx = _scoped.rfind("::");
    assert(idx != string::npos);
    return string(_scoped, 0, idx + 2);
}

string
Slice::Contained::flattenedScope() const
{
    string s = scope();
    string::size_type pos = 0;
    while((pos = s.find("::", pos)) != string::npos)
    {
        s.replace(pos, 2, "_");

    }
    return s;
}

string
Slice::Contained::file() const
{
    return _file;
}

string
Slice::Contained::line() const
{
    return _line;
}

string
Slice::Contained::comment() const
{
    return _comment;
}

namespace
{

void
trimLines(StringList& l)
{
    //
    // Remove empty trailing lines.
    //
    while(!l.empty() && l.back().empty())
    {
        l.pop_back();
    }
}

StringList
splitComment(const string& c, bool stripMarkup)
{
    string comment = c;

    if(stripMarkup)
    {
        //
        // Strip HTML markup and javadoc links.
        //
        string::size_type pos = 0;
        do
        {
            pos = comment.find('<', pos);
            if(pos != string::npos)
            {
                string::size_type endpos = comment.find('>', pos);
                if(endpos == string::npos)
                {
                    break;
                }
                comment.erase(pos, endpos - pos + 1);
            }
        }
        while(pos != string::npos);

        const string link = "{@link";
        pos = 0;
        do
        {
            pos = comment.find(link, pos);
            if(pos != string::npos)
            {
                comment.erase(pos, link.size() + 1); // Erase trailing white space too.
                string::size_type endpos = comment.find('}', pos);
                if(endpos != string::npos)
                {
                    string ident = comment.substr(pos, endpos - pos);
                    comment.erase(pos, endpos - pos + 1);

                    //
                    // Replace links of the form {@link Type#member} with "Type.member".
                    //
                    string::size_type hash = ident.find('#');
                    string rest;
                    if(hash != string::npos)
                    {
                        rest = ident.substr(hash + 1);
                        ident = ident.substr(0, hash);
                        if(!ident.empty())
                        {
                            if(!rest.empty())
                            {
                                ident += "." + rest;
                            }
                        }
                        else if(!rest.empty())
                        {
                            ident = rest;
                        }
                    }

                    comment.insert(pos, ident);
                }
            }
        }
        while(pos != string::npos);
    }

    StringList result;

    string::size_type pos = 0;
    string::size_type nextPos;
    while((nextPos = comment.find_first_of('\n', pos)) != string::npos)
    {
        result.push_back(IceUtilInternal::trim(string(comment, pos, nextPos - pos)));
        pos = nextPos + 1;
    }
    string lastLine = IceUtilInternal::trim(string(comment, pos));
    if(!lastLine.empty())
    {
        result.push_back(lastLine);
    }

    trimLines(result);

    return result;
}

bool
parseCommentLine(const string& l, const string& tag, bool namedTag, string& name, string& doc)
{
    doc.clear();

    if(l.find(tag) == 0)
    {
        const string ws = " \t";

        if(namedTag)
        {
            string::size_type n = l.find_first_not_of(ws, tag.size());
            if(n == string::npos)
            {
                return false; // Malformed line, ignore it.
            }
            string::size_type end = l.find_first_of(ws, n);
            if(end == string::npos)
            {
                return false; // Malformed line, ignore it.
            }
            name = l.substr(n, end - n);
            n = l.find_first_not_of(ws, end);
            if(n != string::npos)
            {
                doc = l.substr(n);
            }
        }
        else
        {
            name.clear();

            string::size_type n = l.find_first_not_of(ws, tag.size());
            if(n == string::npos)
            {
                return false; // Malformed line, ignore it.
            }
            doc = l.substr(n);
        }

        return true;
    }

    return false;
}

}

CommentPtr
Slice::Contained::parseComment(bool stripMarkup) const
{
    CommentPtr comment = new Comment;

    comment->_isDeprecated = false;

    //
    // First check metadata for a deprecated tag.
    //
    string deprecateMetadata;
    if(findMetaData("deprecate", deprecateMetadata))
    {
        comment->_isDeprecated = true;
        if(deprecateMetadata.find("deprecate:") == 0 && deprecateMetadata.size() > 10)
        {
            comment->_deprecated.push_back(IceUtilInternal::trim(deprecateMetadata.substr(10)));
        }
    }

    if(!comment->_isDeprecated && _comment.empty())
    {
        return 0;
    }

    //
    // Split up the comment into lines.
    //
    StringList lines = splitComment(_comment, stripMarkup);

    StringList::const_iterator i;
    for(i = lines.begin(); i != lines.end(); ++i)
    {
        const string l = *i;
        if(l[0] == '@')
        {
            break;
        }
        comment->_overview.push_back(l);
    }

    enum State { StateMisc, StateParam, StateThrows, StateReturn, StateDeprecated };
    State state = StateMisc;
    string name;
    const string ws = " \t";
    const string paramTag = "@param";
    const string throwsTag = "@throws";
    const string exceptionTag = "@exception";
    const string returnTag = "@return";
    const string deprecatedTag = "@deprecated";
    const string seeTag = "@see";
    for(; i != lines.end(); ++i)
    {
        const string l = IceUtilInternal::trim(*i);
        string line;
        if(parseCommentLine(l, paramTag, true, name, line))
        {
            if(!line.empty())
            {
                state = StateParam;
                StringList sl;
                sl.push_back(line); // The first line of the description.
                comment->_parameters[name] = sl;
            }
        }
        else if(parseCommentLine(l, throwsTag, true, name, line))
        {
            if(!line.empty())
            {
                state = StateThrows;
                StringList sl;
                sl.push_back(line); // The first line of the description.
                comment->_exceptions[name] = sl;
            }
        }
        else if(parseCommentLine(l, exceptionTag, true, name, line))
        {
            if(!line.empty())
            {
                state = StateThrows;
                StringList sl;
                sl.push_back(line); // The first line of the description.
                comment->_exceptions[name] = sl;
            }
        }
        else if(parseCommentLine(l, seeTag, false, name, line))
        {
            if(!line.empty())
            {
                comment->_seeAlso.push_back(line);
            }
        }
        else if(parseCommentLine(l, returnTag, false, name, line))
        {
            if(!line.empty())
            {
                state = StateReturn;
                comment->_returns.push_back(line); // The first line of the description.
            }
        }
        else if(parseCommentLine(l, deprecatedTag, false, name, line))
        {
            comment->_isDeprecated = true;
            if(!line.empty())
            {
                state = StateDeprecated;
                comment->_deprecated.push_back(line); // The first line of the description.
            }
        }
        else if(!l.empty())
        {
            if(l[0] == '@')
            {
                //
                // Treat all other tags as miscellaneous comments.
                //
                state = StateMisc;
            }

            switch(state)
            {
                case StateMisc:
                {
                    comment->_misc.push_back(l);
                    break;
                }
                case StateParam:
                {
                    assert(!name.empty());
                    StringList sl;
                    if(comment->_parameters.find(name) != comment->_parameters.end())
                    {
                        sl = comment->_parameters[name];
                    }
                    sl.push_back(l);
                    comment->_parameters[name] = sl;
                    break;
                }
                case StateThrows:
                {
                    assert(!name.empty());
                    StringList sl;
                    if(comment->_exceptions.find(name) != comment->_exceptions.end())
                    {
                        sl = comment->_exceptions[name];
                    }
                    sl.push_back(l);
                    comment->_exceptions[name] = sl;
                    break;
                }
                case StateReturn:
                {
                    comment->_returns.push_back(l);
                    break;
                }
                case StateDeprecated:
                {
                    comment->_deprecated.push_back(l);
                    break;
                }
            }
        }
    }

    trimLines(comment->_overview);
    trimLines(comment->_deprecated);
    trimLines(comment->_misc);
    trimLines(comment->_returns);

    return comment;
}

int
Slice::Contained::includeLevel() const
{
    return _includeLevel;
}

void
Slice::Contained::updateIncludeLevel()
{
    _includeLevel = min(_includeLevel, _unit->currentIncludeLevel());
}

bool
Slice::Contained::hasMetaData(const string& meta) const
{
    return find(_metaData.begin(), _metaData.end(), meta) != _metaData.end();
}

bool
Slice::Contained::findMetaData(const string& prefix, string& meta) const
{
    for(list<string>::const_iterator p = _metaData.begin(); p != _metaData.end(); ++p)
    {
        if(p->find(prefix) == 0)
        {
            meta = *p;
            return true;
        }
    }

    return false;
}

list<string>
Slice::Contained::getMetaData() const
{
    return _metaData;
}

void
Slice::Contained::setMetaData(const list<string>& metaData)
{
    _metaData = metaData;
}

//
// TODO: remove this method once "cs:" and "vb:" prefix are hard errors.
//
void
Slice::Contained::addMetaData(const string& s)
{
    _metaData.push_back(s);
}

FormatType
Slice::Contained::parseFormatMetaData(const list<string>& metaData)
{
    FormatType result = DefaultFormat;

    string tag;
    string prefix = "format:";
    for(list<string>::const_iterator p = metaData.begin(); p != metaData.end(); ++p)
    {
        if(p->find(prefix) == 0)
        {
            tag = *p;
            break;
        }
    }

    if(!tag.empty())
    {
        tag = tag.substr(prefix.size());
        if(tag == "compact")
        {
            result = CompactFormat;
        }
        else if(tag == "sliced")
        {
            result = SlicedFormat;
        }
        else if(tag != "default") // TODO: Allow "default" to be specified as a format value?
        {
            // TODO: How to handle invalid format?
        }
    }

    return result;
}

bool
Slice::Contained::operator<(const Contained& rhs) const
{
    return _scoped < rhs._scoped;
}

bool
Slice::Contained::operator==(const Contained& rhs) const
{
    return _scoped == rhs._scoped;
}

Slice::Contained::Contained(const ContainerPtr& container, const string& name) :
    SyntaxTreeBase(container->unit()),
    _container(container),
    _name(name)
{
    ContainedPtr cont = ContainedPtr::dynamicCast(_container);
    if(cont)
    {
        _scoped = cont->scoped();
    }
    _scoped += "::" + _name;
    assert(_unit);
    _unit->addContent(this);
    _file = _unit->currentFile();
    ostringstream s;
    s << _unit->currentLine();
    _line = s.str();
    _comment = _unit->currentComment();
    _includeLevel = _unit->currentIncludeLevel();
}

// ----------------------------------------------------------------------
// Container
// ----------------------------------------------------------------------

void
Slice::Container::destroy()
{
    for_each(_contents.begin(), _contents.end(), ::IceUtil::voidMemFun(&SyntaxTreeBase::destroy));
    _contents.clear();
    _introducedMap.clear();
    SyntaxTreeBase::destroy();
}

ModulePtr
Slice::Container::createModule(const string& name)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    matches.sort(); // Modules can occur many times...
    matches.unique(); // ... but we only want one instance of each.

    if(thisScope() == "::")
    {
        _unit->addTopLevelModule(_unit->currentFile(), name);
    }

    for(ContainedList::const_iterator p = matches.begin(); p != matches.end(); ++p)
    {
        bool differsOnlyInCase = matches.front()->name() != name;
        ModulePtr module = ModulePtr::dynamicCast(*p);
        if(module)
        {
            if(differsOnlyInCase) // Modules can be reopened only if they are capitalized correctly.
            {
                string msg = "module `" + name + "' is capitalized inconsistently with its previous name: `";
                msg += module->name() + "'";
                _unit->error(msg);
                return 0;
            }
        }
        else if(!differsOnlyInCase)
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as module";
            _unit->error(msg);
            return 0;
        }
        else
        {
            string msg = "module `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " name `" + matches.front()->name() + "'";
            _unit->error(msg);
            return 0;
        }
    }

    if(!nameIsLegal(name, "module"))
    {
        return 0;
    }

    ModulePtr q = new Module(this, name);
    _contents.push_back(q);
    return q;
}

ClassDefPtr
Slice::Container::createClassDef(const string& name, int id, bool intf, const ClassList& bases, bool local)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    for(ContainedList::const_iterator p = matches.begin(); p != matches.end(); ++p)
    {
        ClassDeclPtr decl = ClassDeclPtr::dynamicCast(*p);
        if(decl)
        {
            if(checkInterfaceAndLocal(name, false, intf, decl->isInterface(), local, decl->isLocal()))
            {
                continue;
            }
            return 0;
        }

        bool differsOnlyInCase = matches.front()->name() != name;
        ClassDefPtr def = ClassDefPtr::dynamicCast(*p);
        if(def)
        {
            if(differsOnlyInCase)
            {
                string msg = intf ? "interface" : "class";
                msg += " definition `" + name + "' is capitalized inconsistently with its previous name: `";
                msg += def->name() + "'";
                _unit->error(msg);
            }
            else
            {
                if(_unit->ignRedefs())
                {
                    def->updateIncludeLevel();
                    return def;
                }

                string msg = "redefinition of ";
                msg += intf ? "interface" : "class";
                msg += " `" + name + "'";
                _unit->error(msg);
            }
        }
        else if(differsOnlyInCase)
        {
            string msg = intf ? "interface" : "class";
            msg = " definition `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " name `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        else
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name() + "' as ";
            msg += intf ? "interface" : "class";
            _unit->error(msg);
        }
        return 0;
    }

    if(!nameIsLegal(name, intf ? "interface" : "class"))
    {
        return 0;
    }

    if(!checkForGlobalDef(name, intf ? "interface" : "class"))
    {
        return 0;
    }

    ClassDecl::checkBasesAreLegal(name, intf, local, bases, _unit);

    ClassDefPtr def = new ClassDef(this, name, id, intf, bases, local);
    _contents.push_back(def);

    for(ContainedList::const_iterator q = matches.begin(); q != matches.end(); ++q)
    {
        ClassDeclPtr decl = ClassDeclPtr::dynamicCast(*q);
        decl->_definition = def;
    }

    //
    // Implicitly create a class declaration for each class
    // definition. This way the code generator can rely on always
    // having a class declaration available for lookup.
    //
    ClassDeclPtr decl = createClassDecl(name, intf, local);
    def->_declaration = decl;

    return def;
}

ClassDeclPtr
Slice::Container::createClassDecl(const string& name, bool intf, bool local)
{
    ClassDefPtr def;

    ContainedList matches = _unit->findContents(thisScope() + name);
    for(ContainedList::const_iterator p = matches.begin(); p != matches.end(); ++p)
    {
        ClassDefPtr clDef = ClassDefPtr::dynamicCast(*p);
        if(clDef)
        {
            if(checkInterfaceAndLocal(name, true, intf, clDef->isInterface(), local, clDef->isLocal()))
            {
                assert(!def);
                def = clDef;
                continue;
            }
            return 0;
        }

        ClassDeclPtr clDecl = ClassDeclPtr::dynamicCast(*p);
        if(clDecl)
        {
            if(checkInterfaceAndLocal(name, false, intf, clDecl->isInterface(), local, clDecl->isLocal()))
            {
                continue;
            }
            return 0;
        }

        bool differsOnlyInCase = matches.front()->name() != name;
        if(differsOnlyInCase)
        {
            string msg = "class declaration `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " name `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        else
        {
            string msg = "declaration of already defined `";
            msg += name;
            msg += "' as ";
            msg += intf ? "interface" : "class";
            _unit->error(msg);
            return 0;
        }
    }

    if(!nameIsLegal(name, intf ? "interface" : "class"))
    {
        return 0;
    }

    if(!checkForGlobalDef(name, intf ? "interface" : "class"))
    {
        return 0;
    }

    //
    // Multiple declarations are permissible. But if we do already
    // have a declaration for the class in this container, we don't
    // create another one.
    //
    for(ContainedList::const_iterator q = _contents.begin(); q != _contents.end(); ++q)
    {
        if((*q)->name() == name)
        {
            ClassDeclPtr decl = ClassDeclPtr::dynamicCast(*q);
            if(decl)
            {
                return decl;
            }

            assert(ClassDefPtr::dynamicCast(*q));
        }
    }

    _unit->currentContainer();
    ClassDeclPtr decl = new ClassDecl(this, name, intf, local);
    _contents.push_back(decl);

    if(def)
    {
        decl->_definition = def;
    }

    return decl;
}

ExceptionPtr
Slice::Container::createException(const string& name, const ExceptionPtr& base, bool local, NodeType nt)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        ExceptionPtr p = ExceptionPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() == name)
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as exception";
            _unit->error(msg);
        }
        else
        {
            string msg = "exception `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        return 0;
    }

    nameIsLegal(name, "exception"); // Don't return here -- we create the exception anyway

    if(nt == Real)
    {
        checkForGlobalDef(name, "exception"); // Don't return here -- we create the exception anyway
    }

    //
    // If this definition is non-local, base cannot be local.
    //
    if(!local && base && base->isLocal())
    {
        _unit->error("non-local exception `" + name + "' cannot have local base exception `" + base->name() + "'");
    }

    ExceptionPtr p = new Exception(this, name, base, local);
    _contents.push_back(p);
    return p;
}

StructPtr
Slice::Container::createStruct(const string& name, bool local, NodeType nt)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        StructPtr p = StructPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() == name)
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as struct";
            _unit->error(msg);
        }
        else
        {
            string msg = "struct `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        return 0;
    }

    nameIsLegal(name, "structure"); // Don't return here -- we create the struct anyway.

    if(nt == Real)
    {
        checkForGlobalDef(name, "structure"); // Don't return here -- we create the struct anyway.
    }

    StructPtr p = new Struct(this, name, local);
    _contents.push_back(p);
    return p;
}

SequencePtr
Slice::Container::createSequence(const string& name, const TypePtr& type, const StringList& metaData, bool local,
                                 NodeType nt)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        SequencePtr p = SequencePtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() == name)
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as sequence";
            _unit->error(msg);
        }
        else
        {
            string msg = "sequence `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        return 0;
    }

    nameIsLegal(name, "sequence"); // Don't return here -- we create the sequence anyway.

    if(nt == Real)
    {
        checkForGlobalDef(name, "sequence"); // Don't return here -- we create the sequence anyway.
    }

    //
    // If sequence is non-local, element type cannot be local.
    //
    if(!local && type->isLocal())
    {
        string msg = "non-local sequence `" + name + "' cannot have local element type";
        _unit->error(msg);
    }

    checkDeprecatedType(_unit, type);

    SequencePtr p = new Sequence(this, name, type, metaData, local);
    _contents.push_back(p);
    return p;
}

DictionaryPtr
Slice::Container::createDictionary(const string& name, const TypePtr& keyType, const StringList& keyMetaData,
                                   const TypePtr& valueType, const StringList& valueMetaData, bool local,
                                   NodeType nt)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        DictionaryPtr p = DictionaryPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() == name)
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as dictionary";
            _unit->error(msg);
        }
        else
        {
            string msg = "dictionary `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        return 0;
    }

    nameIsLegal(name, "dictionary"); // Don't return here -- we create the dictionary anyway.

    if(nt == Real)
    {
        checkForGlobalDef(name, "dictionary"); // Don't return here -- we create the dictionary anyway.
    }

    if(nt == Real)
    {
        bool containsSequence = false;
        if(!Dictionary::legalKeyType(keyType, containsSequence))
        {
            _unit->error("dictionary `" + name + "' uses an illegal key type");
            return 0;
        }
        if(containsSequence)
        {
            _unit->warning(Deprecated, "use of sequences in dictionary keys has been deprecated");
        }
    }

    if(!local)
    {
        if(keyType->isLocal())
        {
            string msg = "non-local dictionary `" + name + "' cannot have local key type";
            _unit->error(msg);
        }
        if(valueType->isLocal())
        {
            string msg = "non-local dictionary `" + name + "' cannot have local value type";
            _unit->error(msg);
        }
    }

    checkDeprecatedType(_unit, valueType);

    DictionaryPtr p = new Dictionary(this, name, keyType, keyMetaData, valueType, valueMetaData, local);
    _contents.push_back(p);
    return p;
}

EnumPtr
Slice::Container::createEnum(const string& name, bool local, NodeType nt)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        EnumPtr p = EnumPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() == name)
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as enumeration";
            _unit->error(msg);
        }
        else
        {
            string msg = "enumeration `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        return 0;
    }

    nameIsLegal(name, "enumeration"); // Don't return here -- we create the enumeration anyway.

    if(nt == Real)
    {
        checkForGlobalDef(name, "enumeration"); // Don't return here -- we create the enumeration anyway.
    }

    EnumPtr p = new Enum(this, name, local);
    _contents.push_back(p);
    return p;
}

EnumeratorPtr
Slice::Container::createEnumerator(const string& name)
{
    EnumeratorPtr p = validateEnumerator(name);
    if(!p)
    {
        p = new Enumerator(this, name);
        _contents.push_back(p);
    }
    return p;
}

EnumeratorPtr
Slice::Container::createEnumerator(const string& name, int value)
{
    EnumeratorPtr p = validateEnumerator(name);
    if(!p)
    {
        p = new Enumerator(this, name, value);
        _contents.push_back(p);
    }
    return p;
}

ConstPtr
Slice::Container::createConst(const string name, const TypePtr& constType, const StringList& metaData,
                              const SyntaxTreeBasePtr& valueType, const string& value, const string& literal,
                              NodeType nt)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        ConstPtr p = ConstPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() == name)
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as constant";
            _unit->error(msg);
        }
        else
        {
            string msg = "constant `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        return 0;
    }

    nameIsLegal(name, "constant"); // Don't return here -- we create the constant anyway.

    if(nt == Real)
    {
        checkForGlobalDef(name, "constant"); // Don't return here -- we create the constant anyway.
    }

    SyntaxTreeBasePtr resolvedValueType = valueType;

    //
    // Validate the constant and its value; for enums, find enumerator
    //
    if(nt == Real && !validateConstant(name, constType, resolvedValueType, value, true))
    {
        return 0;
    }

    ConstPtr p = new Const(this, name, constType, metaData, resolvedValueType, value, literal);
    _contents.push_back(p);
    return p;
}

TypeList
Slice::Container::lookupType(const string& scoped, bool printError)
{
    //
    // Remove whitespace.
    //
    string sc = scoped;
    string::size_type pos;
    while((pos = sc.find_first_of(" \t\r\n")) != string::npos)
    {
        sc.erase(pos, 1);
    }

    //
    // Check for builtin type.
    //
    for(unsigned int i = 0; i < sizeof(Builtin::builtinTable) / sizeof(const char*); ++i)
    {
        if(sc == Builtin::builtinTable[i])
        {
            TypeList result;
            result.push_back(_unit->builtin(static_cast<Builtin::Kind>(i)));
            return result;
        }
    }

    //
    // Not a builtin type, try to look up a constructed type.
    //
    return lookupTypeNoBuiltin(scoped, printError);
}

TypeList
Slice::Container::lookupTypeNoBuiltin(const string& scoped, bool printError, bool ignoreUndefined)
{
    //
    // Remove whitespace.
    //
    string sc = scoped;
    string::size_type pos;
    while((pos = sc.find_first_of(" \t\r\n")) != string::npos)
    {
        sc.erase(pos, 1);
    }

    //
    // Absolute scoped name?
    //
    if(sc.size() >= 2 && sc[0] == ':')
    {
        return _unit->lookupTypeNoBuiltin(sc.substr(2), printError);
    }

    TypeList results;
    bool typeError = false;
    vector<string> errors;
    if(sc.rfind('*') == sc.length() - 1)
    {
        //
        // Proxies.
        //
        ContainedList matches = _unit->findContents(thisScope() + sc.substr(0, sc.length() - 1));
        for(ContainedList::const_iterator p = matches.begin(); p != matches.end(); ++p)
        {
            ClassDefPtr def = ClassDefPtr::dynamicCast(*p);
            if(def)
            {
                continue; // Ignore class definitions.
            }

            if(printError && matches.front()->scoped() != (thisScope() + sc))
            {
                string msg = (*p)->kindOf() + " name `" + scoped;
                msg += "' is capitalized inconsistently with its previous name: `";
                msg += matches.front()->scoped() + "'";
                _unit->error(msg);
            }

            ClassDeclPtr cl = ClassDeclPtr::dynamicCast(*p);
            if(!cl)
            {
                if(printError)
                {
                    string msg = "`";
                    msg += sc;
                    msg += "' must be class or interface";
                    _unit->error(msg);
                }
                return TypeList();
            }
            results.push_back(new Proxy(cl));
        }
    }
    else
    {
        //
        // Non-Proxies.
        //
        ContainedList matches = _unit->findContents(thisScope() + sc);
        for(ContainedList::const_iterator p = matches.begin(); p != matches.end(); ++p)
        {
            ClassDefPtr def = ClassDefPtr::dynamicCast(*p);
            if(def)
            {
                continue; // Ignore class definitions.
            }

            if(printError && matches.front()->scoped() != (thisScope() + sc))
            {
                string msg = (*p)->kindOf() + " name `" + scoped;
                msg += "' is capitalized inconsistently with its previous name: `";
                msg += matches.front()->scoped() + "'";
                errors.push_back(msg);
            }

            ExceptionPtr ex = ExceptionPtr::dynamicCast(*p);
            if(ex)
            {
                if(printError)
                {
                    string msg = "`";
                    msg += sc;
                    msg += "' is an exception, which cannot be used as a type";
                    _unit->error(msg);
                }
                return TypeList();
            }

            TypePtr type = TypePtr::dynamicCast(*p);
            if(!type)
            {
                typeError = true;
                if(printError)
                {
                    string msg = "`";
                    msg += sc;
                    msg += "' is not a type";
                    errors.push_back(msg);
                }
                break; // Possible that correct match is higher in scope
            }
            results.push_back(type);
        }
    }

    if(results.empty())
    {
        ContainedPtr contained = ContainedPtr::dynamicCast(this);
        if(contained)
        {
            results = contained->container()->lookupTypeNoBuiltin(sc, printError, typeError || ignoreUndefined);
        }
        else if(!typeError)
        {
            if(printError && !ignoreUndefined)
            {
                string msg = "`";
                msg += sc;
                msg += "' is not defined";
                _unit->error(msg);
            }
            return TypeList();
        }
    }

    //
    // Do not emit errors if there was a type error but a match was found in a higher scope.
    //
    if(printError && !(typeError && !results.empty()))
    {
        for(vector<string>::const_iterator p = errors.begin(); p != errors.end(); ++p)
        {
            _unit->error(*p);
        }
    }
    return results;
}

ContainedList
Slice::Container::lookupContained(const string& scoped, bool printError)
{
    //
    // Remove whitespace.
    //
    string sc = scoped;
    string::size_type pos;
    while((pos = sc.find_first_of(" \t\r\n")) != string::npos)
    {
        sc.erase(pos, 1);
    }

    //
    // Absolute scoped name?
    //
    if(sc.size() >= 2 && sc[0] == ':')
    {
        return _unit->lookupContained(sc.substr(2), printError);
    }

    ContainedList matches = _unit->findContents(thisScope() + sc);
    ContainedList results;
    for(ContainedList::const_iterator p = matches.begin(); p != matches.end(); ++p)
    {
        if(!ClassDefPtr::dynamicCast(*p)) // Ignore class definitions.
        {
            results.push_back(*p);

            if(printError && (*p)->scoped() != (thisScope() + sc))
            {
                string msg = (*p)->kindOf() + " name `" + scoped;
                msg += "' is capitalized inconsistently with its previous name: `" + (*p)->scoped() + "'";
                _unit->error(msg);
            }
        }
    }

    if(results.empty())
    {
        ContainedPtr contained = ContainedPtr::dynamicCast(this);
        if(!contained)
        {
            if(printError)
            {
                string msg = "`";
                msg += sc;
                msg += "' is not defined";
                _unit->error(msg);
            }
            return ContainedList();
        }
        return contained->container()->lookupContained(sc, printError);
    }
    else
    {
        return results;
    }
}

ExceptionPtr
Slice::Container::lookupException(const string& scoped, bool printError)
{
    ContainedList contained = lookupContained(scoped, printError);
    if(contained.empty())
    {
        return 0;
    }

    ExceptionList exceptions;
    for(ContainedList::iterator p = contained.begin(); p != contained.end(); ++p)
    {
        ExceptionPtr ex = ExceptionPtr::dynamicCast(*p);
        if(!ex)
        {
            if(printError)
            {
                string msg = "`";
                msg += scoped;
                msg += "' is not an exception";
                _unit->error(msg);
            }
            return 0;
        }
        exceptions.push_back(ex);
    }
    assert(exceptions.size() == 1);
    return exceptions.front();
}

UnitPtr
Slice::Container::unit() const
{
    return SyntaxTreeBase::unit();
}

ModuleList
Slice::Container::modules() const
{
    ModuleList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ModulePtr q = ModulePtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

ClassList
Slice::Container::classes() const
{
    ClassList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr q = ClassDefPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

ExceptionList
Slice::Container::exceptions() const
{
    ExceptionList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ExceptionPtr q = ExceptionPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

StructList
Slice::Container::structs() const
{
    StructList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        StructPtr q = StructPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

SequenceList
Slice::Container::sequences() const
{
    SequenceList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        SequencePtr q = SequencePtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

DictionaryList
Slice::Container::dictionaries() const
{
    DictionaryList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DictionaryPtr q = DictionaryPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

EnumList
Slice::Container::enums() const
{
    EnumList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        EnumPtr q = EnumPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

EnumeratorList
Slice::Container::enumerators() const
{
    EnumeratorList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        EnumeratorPtr q = EnumeratorPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

//
// Find enumerators using the old unscoped enumerators lookup
//
EnumeratorList
Slice::Container::enumerators(const string& scoped) const
{
    EnumeratorList result;
    string::size_type lastColon = scoped.rfind(':');

    if(lastColon == string::npos)
    {
        // check all enclosing scopes
        ContainerPtr container = const_cast<Container*>(this);
        do
        {
            EnumList enums = container->enums();
            for(EnumList::iterator p = enums.begin(); p != enums.end(); ++p)
            {
                ContainedList cl = (*p)->lookupContained(scoped, false);
                if(!cl.empty())
                {
                    result.push_back(EnumeratorPtr::dynamicCast(cl.front()));
                }
            }

            ContainedPtr contained = ContainedPtr::dynamicCast(container);
            if(contained)
            {
                container = contained->container();
            }
            else
            {
                container = 0;
            }
        }
        while(result.empty() && container);
    }
    else
    {
        // Find the referenced scope
        ContainerPtr container = const_cast<Container*>(this);
        string scope = scoped.substr(0, scoped.rfind("::"));
        ContainedList cl = container->lookupContained(scope, false);
        if(!cl.empty())
        {
            container = ContainerPtr::dynamicCast(cl.front());
            if(container)
            {
                EnumList enums = container->enums();
                string name = scoped.substr(lastColon + 1);
                for(EnumList::iterator p = enums.begin(); p != enums.end(); ++p)
                {
                    ContainedList cl2 = (*p)->lookupContained(name, false);
                    if(!cl2.empty())
                    {
                        result.push_back(EnumeratorPtr::dynamicCast(cl2.front()));
                    }
                }
            }
        }
    }

    return result;
}

ConstList
Slice::Container::consts() const
{
    ConstList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ConstPtr q = ConstPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

ContainedList
Slice::Container::contents() const
{
    return _contents;
}

bool
Slice::Container::hasNonLocalClassDecls() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDeclPtr cl = ClassDeclPtr::dynamicCast(*p);
        if(cl && !cl->isLocal())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalClassDecls())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasNonLocalClassDefs() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && !cl->isLocal())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalClassDefs())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasLocalClassDefsWithAsync() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && cl->isLocal())
        {
            if(cl->hasMetaData("async-oneway"))
            {
                return true;
            }

            OperationList ol = cl->operations();
            for(OperationList::const_iterator q = ol.begin(); q != ol.end(); ++q)
            {
                if((*q)->hasMetaData("async-oneway"))
                {
                    return true;
                }
            }
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasLocalClassDefsWithAsync())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasNonLocalSequences() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        SequencePtr s = SequencePtr::dynamicCast(*p);
        if(s && !s->isLocal())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalSequences())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasNonLocalExceptions() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ExceptionPtr q = ExceptionPtr::dynamicCast(*p);
        if(q && !q->isLocal())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalExceptions())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasStructs() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        StructPtr q = StructPtr::dynamicCast(*p);
        if(q)
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasStructs())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasExceptions() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ExceptionPtr q = ExceptionPtr::dynamicCast(*p);
        if(q)
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasExceptions())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasClassDecls() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        if(ClassDeclPtr::dynamicCast(*p))
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasClassDecls())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasDictionaries() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DictionaryPtr d = DictionaryPtr::dynamicCast(*p);
        if(d)
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasDictionaries())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasOnlyDictionaries(DictionaryList& dicts) const
{
    bool ret = true;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ModulePtr m = ModulePtr::dynamicCast(*p);
        if(m)
        {
            bool subret = m->hasOnlyDictionaries(dicts);
            if(!subret && ret)
            {
                ret = false;
            }
        }
        DictionaryPtr d = DictionaryPtr::dynamicCast(*p);
        if(d && ret)
        {
            dicts.push_back(d);
        }
        else
        {
            ret = false;
        }
    }

    if(!ret)
    {
        dicts.clear();
    }

    return ret;
}

bool
Slice::Container::hasClassDefs() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        if(ClassDefPtr::dynamicCast(*p))
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasClassDefs())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasLocalClassDefs() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && cl->isLocal())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasLocalClassDefs())
        {
            return true;
        }
    }
    return false;
}

bool
Slice::Container::hasNonLocalInterfaceDefs() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && !cl->isLocal() && (cl->isInterface() || !cl->allOperations().empty()))
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalInterfaceDefs())
        {
            return true;
        }
    }
    return false;
}

bool
Slice::Container::hasValueDefs() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && !cl->isLocal() && !cl->isInterface())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasValueDefs())
        {
            return true;
        }
    }
    return false;
}

bool
Slice::Container::hasOnlyClassDecls() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ModulePtr m = ModulePtr::dynamicCast(*p);
        if(m)
        {
            if(!m->hasOnlyClassDecls())
            {
                return false;
            }
        }
        else if(!ClassDeclPtr::dynamicCast(*p))
        {
            return false;
        }
    }

    return true;
}

bool
Slice::Container::hasOperations() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && cl->hasOperations())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasOperations())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasNonLocalAbstractClassDefs() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && cl->isAbstract() && !cl->isLocal())
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalAbstractClassDefs())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasNonLocalDataOnlyClasses() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr q = ClassDefPtr::dynamicCast(*p);
        if(q)
        {
            if(!q->isLocal() && !q->isAbstract())
            {
                return true;
            }
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalDataOnlyClasses())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasOtherConstructedOrExceptions() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        if(ConstructedPtr::dynamicCast(*p) && !ClassDeclPtr::dynamicCast(*p) && !ClassDefPtr::dynamicCast(*p))
        {
            return true;
        }

        if(ExceptionPtr::dynamicCast(*p))
        {
            return true;
        }

        if(ConstPtr::dynamicCast(*p))
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasOtherConstructedOrExceptions())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasContentsWithMetaData(const string& meta) const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        if((*p)->hasMetaData(meta))
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasContentsWithMetaData(meta))
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasAsyncOps() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ClassDefPtr cl = ClassDefPtr::dynamicCast(*p);
        if(cl && !cl->isLocal())
        {
            OperationList ops = cl->operations();
            if(!ops.empty() && cl->hasMetaData("amd"))
            {
                return true;
            }
            for(OperationList::const_iterator i = ops.begin(); i != ops.end(); ++i)
            {
                OperationPtr op = *i;
                if(op->hasMetaData("amd"))
                {
                    return true;
                }
            }
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasAsyncOps())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Container::hasNonLocalContained(Contained::ContainedType type) const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        if((*p)->containedType() == type)
        {
            return true;
        }

        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container && container->hasNonLocalContained(type))
        {
            return true;
        }
    }

    return false;
}

string
Slice::Container::thisScope() const
{
    string s;
    ContainedPtr contained = ContainedPtr::dynamicCast(const_cast<Container*>(this));
    if(contained)
    {
        s = contained->scoped();
    }
    s += "::";
    return s;
}

void
Slice::Container::mergeModules()
{
    for(ContainedList::iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ModulePtr mod1 = ModulePtr::dynamicCast(*p);
        if(!mod1)
        {
            continue;
        }

        DefinitionContextPtr dc1 = mod1->definitionContext();
        assert(dc1);
        StringList metaData1 = dc1->getMetaData();
        metaData1.sort();
        metaData1.unique();

        ContainedList::iterator q = p;
        ++q;
        while(q != _contents.end())
        {
            ModulePtr mod2 = ModulePtr::dynamicCast(*q);
            if(!mod2)
            {
                ++q;
                continue;
            }

            if(mod1->name() != mod2->name())
            {
                ++q;
                continue;
            }

            //
            // Compare the file metadata of the two modules being merged.
            //
            DefinitionContextPtr dc2 = mod2->definitionContext();
            assert(dc2);
            StringList metaData2 = dc2->getMetaData();
            metaData2.sort();
            metaData2.unique();
            if(!checkGlobalMetaData(metaData1, metaData2))
            {
                unit()->warning(All, "file metadata mismatch for module `" + mod1->name() + "' in files " +
                                dc1->filename() + " and " + dc2->filename());
            }

            mod1->_contents.splice(mod1->_contents.end(), mod2->_contents);

            if(mod1->_comment.length() < mod2->_comment.length())
            {
                mod1->_comment.swap(mod2->_comment);
            }

            mod1->_includeLevel = min(mod1->_includeLevel, mod2->_includeLevel);

            _unit->removeContent(*q);
            q = _contents.erase(q);
        }

        mod1->mergeModules();
    }
}

void
Slice::Container::sort()
{
    _contents.sort();
}

void
Slice::Container::sortContents(bool sortFields)
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ContainerPtr container = ContainerPtr::dynamicCast(*p);
        if(container)
        {
            if(!sortFields)
            {
                if(StructPtr::dynamicCast(container) ||
                   ClassDefPtr::dynamicCast(container) ||
                   ExceptionPtr::dynamicCast(container))
                {
                    continue;
                }
            }
            //
            // Don't sort operation definitions, otherwise parameters are shown in the
            // wrong order in the synopsis.
            //
            if(!OperationPtr::dynamicCast(container))
            {
                container->sort();
            }
            container->sortContents(sortFields);
        }
    }
}

void
Slice::Container::visit(ParserVisitor* visitor, bool all)
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        if(all || (*p)->includeLevel() == 0)
        {
            (*p)->visit(visitor, all);
        }
    }
}

void
Slice::Container::containerRecDependencies(set<ConstructedPtr>& dependencies)
{
    for(ContainedList::iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ConstructedPtr constructed = ConstructedPtr::dynamicCast(*p);
        if(constructed && dependencies.find(constructed) != dependencies.end())
        {
            dependencies.insert(constructed);
            constructed->recDependencies(dependencies);
        }
    }
}

bool
Slice::Container::checkIntroduced(const string& scoped, ContainedPtr namedThing)
{
    if(scoped[0] == ':') // Only unscoped names introduce anything.
    {
        return true;
    }

    //
    // Split off first component.
    //
    string::size_type pos = scoped.find("::");
    string firstComponent = pos == string::npos ? scoped : scoped.substr(0, pos);

    //
    // If we don't have a type, the thing that is introduced is the contained for
    // the first component.
    //
    if(namedThing == 0)
    {
        ContainedList cl = lookupContained(firstComponent, false);
        if(cl.empty())
        {
            return true; // Ignore types whose creation failed previously.
        }
        namedThing = cl.front();
    }
    else
    {
        //
        // For each scope, get the container until we have the container
        // for the first scope (which is the introduced one).
        //
        ContainerPtr c;
        bool first = true;
        while(pos != string::npos)
        {
            if(first)
            {
                c = namedThing->container();
            }
            else
            {
                ContainedPtr contained = ContainedPtr::dynamicCast(c);
                if(contained)
                {
                    c = contained->container();
                }
            }
            first = false;
            if(pos != string::npos)
            {
                pos = scoped.find("::", pos + 2);
            }
        }
        if(ContainedPtr::dynamicCast(c))
        {
            namedThing = ContainedPtr::dynamicCast(c);
        }
    }

    //
    // Check if the first component is in the introduced map of this scope.
    //
    map<string, ContainedPtr, CICompare>::const_iterator it = _introducedMap.find(firstComponent);
    if(it == _introducedMap.end())
    {
        //
        // We've just introduced the first component to the current scope.
        //
        _introducedMap[firstComponent] = namedThing;    // No, insert it
    }
    else
    {
        //
        // We've previously introduced the first component to the current scope,
        // check that it has not changed meaning.
        //
        if(it->second != namedThing)
        {
            //
            // Parameter are in its own scope.
            //
            if((ParamDeclPtr::dynamicCast(it->second) && !ParamDeclPtr::dynamicCast(namedThing)) ||
               (!ParamDeclPtr::dynamicCast(it->second) && ParamDeclPtr::dynamicCast(namedThing)))
            {
                return true;
            }

            //
            // Data members are in its own scope.
            //
            if((DataMemberPtr::dynamicCast(it->second) && !DataMemberPtr::dynamicCast(namedThing)) ||
               (!DataMemberPtr::dynamicCast(it->second) && DataMemberPtr::dynamicCast(namedThing)))
            {
                return true;
            }

            _unit->error("`" + firstComponent + "' has changed meaning");
            return false;
        }
    }
    return true;
}

bool
Slice::Container::nameIsLegal(const string& newName, const char* newConstruct)
{
    ModulePtr module = ModulePtr::dynamicCast(this);

    //
    // Check whether the enclosing module has the same name.
    //
    if(module)
    {
        if(newName == module->name())
        {
            string msg = newConstruct;
            msg += " name `" + newName + "' must differ from the name of its immediately enclosing module";
            _unit->error(msg);
            return false;
        }

        string name = IceUtilInternal::toLower(newName);
        string thisName = IceUtilInternal::toLower(module->name());
        if(name == thisName)
        {
            string msg = newConstruct;
            msg += " name `" + name + "' cannot differ only in capitalization from its immediately enclosing "
                   "module name `" + module->name() + "'";
            _unit->error(msg);
            return false;
        }

        module = ModulePtr::dynamicCast(module->container()); // Get enclosing module for test below.
    }

    //
    // Check whether any of the enclosing modules have the same name.
    //
    while(module)
    {
        if(newName == module->name())
        {
            string msg = newConstruct;
            msg += " name `" + newName + "' must differ from the name of enclosing module `" + module->name()
                   + "' (first defined at " + module->file() + ":" + module->line() + ")";
            _unit->error(msg);
            return false;
        }

        string name = IceUtilInternal::toLower(newName);
        string thisName = IceUtilInternal::toLower(module->name());
        if(name == thisName)
        {
            string msg = newConstruct;
            msg += " name `" + name + "' cannot differ only in capitalization from enclosing module `"
                   + module->name() + "' (first defined at " + module->file() + ":" + module->line() + ")";
            _unit->error(msg);
            return false;
        }

        module = ModulePtr::dynamicCast(module->container());
    }

    return true;
}

bool
Slice::Container::checkForGlobalDef(const string& name, const char* newConstruct)
{
    if(dynamic_cast<Unit*>(this) && strcmp(newConstruct, "module"))
    {
        static const string vowels = "aeiou";
        string glottalStop;
        if(vowels.find_first_of(newConstruct[0]) != string::npos)
        {
            glottalStop = "n";
        }
        _unit->error("`" + name + "': a" + glottalStop + " " + newConstruct +
                     " can be defined only at module scope");
        return false;
    }
    return true;
}

Slice::Container::Container(const UnitPtr& unit) :
    SyntaxTreeBase(unit)
{
}

bool
Slice::Container::checkInterfaceAndLocal(const string& name, bool defined,
                                         bool intf, bool intfOther,
                                         bool local, bool localOther)
{
    string definedOrDeclared;
    if(defined)
    {
        definedOrDeclared = "defined";
    }
    else
    {
        definedOrDeclared = "declared";
    }

    if(!intf && intfOther)
    {
        string msg = "class `";
        msg += name;
        msg += "' was ";
        msg += definedOrDeclared;
        msg += " as interface";
        _unit->error(msg);
        return false;
    }

    if(intf && !intfOther)
    {
        string msg = "interface `";
        msg += name;
        msg += "' was ";
        msg += definedOrDeclared;
        msg += " as class";
        _unit->error(msg);
        return false;
    }

    if(!local && localOther)
    {
        string msg = "non-local `";
        msg += name;
        msg += "' was ";
        msg += definedOrDeclared;
        msg += " local";
        _unit->error(msg);
        return false;
    }

    if(local && !localOther)
    {
        string msg = "local `";
        msg += name;
        msg += "' was ";
        msg += definedOrDeclared;
        msg += " non-local";
        _unit->error(msg);
        return false;
    }

    return true;
}

bool
Slice::Container::checkGlobalMetaData(const StringList& m1, const StringList& m2)
{
    //
    // Not all file metadata mismatches represent actual problems. We are only concerned about
    // the prefixes listed below (also see bug 2766).
    //
    static const char* prefixes[] =
    {
        "java:package",
        "python:package",
        0
    };

    //
    // Collect the metadata that is unique to each list.
    //
    StringList diffs;
    set_symmetric_difference(m1.begin(), m1.end(), m2.begin(), m2.end(), back_inserter(diffs));

    for(StringList::const_iterator p = diffs.begin(); p != diffs.end(); ++p)
    {
        for(int i = 0; prefixes[i] != 0; ++i)
        {
            if(p->find(prefixes[i]) != string::npos)
            {
                return false;
            }
        }
    }

    return true;
}

bool
Slice::Container::validateConstant(const string& name, const TypePtr& type, SyntaxTreeBasePtr& valueType,
                                   const string& value, bool isConstant)
{
    //
    // isConstant indicates whether a constant or a data member (with a default value) is
    // being defined.
    //

    if(!type)
    {
        return false;
    }

    const string desc = isConstant ? "constant" : "data member";

    //
    // If valueType is a ConstPtr, it means the constant or data member being defined
    // refers to another constant.
    //
    const ConstPtr constant = ConstPtr::dynamicCast(valueType);

    //
    // First verify that it is legal to specify a constant or default value for the given type.
    //

    BuiltinPtr b = BuiltinPtr::dynamicCast(type);
    EnumPtr e = EnumPtr::dynamicCast(type);

    if(b)
    {
        switch(b->kind())
        {
            case Builtin::KindBool:
            case Builtin::KindByte:
            case Builtin::KindShort:
            case Builtin::KindInt:
            case Builtin::KindLong:
            case Builtin::KindFloat:
            case Builtin::KindDouble:
            case Builtin::KindString:
                break;
            default:
            {
                if(isConstant)
                {
                    _unit->error("constant `" + name + "' has illegal type: `" + b->kindAsString() + "'");
                }
                else
                {
                    _unit->error("default value not allowed for data member `" + name + "' of type `" +
                                 b->kindAsString() + "'");
                }
                return false;
            }
        }
    }
    else if(!e)
    {
        if(isConstant)
        {
            _unit->error("constant `" + name + "' has illegal type");
        }
        else
        {
            _unit->error("default value not allowed for data member `" + name + "'");
        }
        return false;
    }

    //
    // Next, verify that the type of the constant or data member is compatible with the given value.
    //

    if(b)
    {
        BuiltinPtr lt;

        if(constant)
        {
            lt = BuiltinPtr::dynamicCast(constant->type());
        }
        else
        {
            lt = BuiltinPtr::dynamicCast(valueType);
        }

        if(lt)
        {
            bool ok = true;
            switch(b->kind())
            {
                case Builtin::KindBool:
                {
                    if(lt->kind() != Builtin::KindBool)
                    {
                        ok = false;
                    }
                    break;
                }
                case Builtin::KindByte:
                case Builtin::KindShort:
                case Builtin::KindInt:
                case Builtin::KindLong:
                {
                    switch(lt->kind())
                    {
                    case Builtin::KindByte:
                    case Builtin::KindShort:
                    case Builtin::KindInt:
                    case Builtin::KindLong:
                        break;
                    default:
                        ok = false;
                        break;
                    }
                    break;
                }
                case Builtin::KindFloat:
                case Builtin::KindDouble:
                {
                    switch(lt->kind())
                    {
                    case Builtin::KindByte:
                    case Builtin::KindShort:
                    case Builtin::KindInt:
                    case Builtin::KindLong:
                    case Builtin::KindFloat:
                    case Builtin::KindDouble:
                        break;
                    default:
                        ok = false;
                        break;
                    }
                    break;
                }
                case Builtin::KindString:
                {
                    if(lt->kind() != Builtin::KindString)
                    {
                        ok = false;
                    }
                    break;
                }
                case Builtin::KindObject:
                case Builtin::KindObjectProxy:
                case Builtin::KindLocalObject:
                case Builtin::KindValue:
                {
                    assert(false);
                    break;
                }
            }

            if(!ok)
            {
                string msg = "initializer of type `" + lt->kindAsString() + "' is incompatible with the type `" +
                    b->kindAsString() + "' of " + desc + " `" + name + "'";
                _unit->error(msg);
                return false;
            }
        }
        else
        {
            string msg = "type of initializer is incompatible with the type `" + b->kindAsString() + "' of " + desc +
                " `" + name + "'";
            _unit->error(msg);
            return false;
        }

        switch(b->kind())
        {
            case Builtin::KindByte:
            {
                IceUtil::Int64 l = IceUtilInternal::strToInt64(value.c_str(), 0, 0);
                if(l < ByteMin || l > ByteMax)
                {
                    string msg = "initializer `" + value + "' for " + desc + " `" + name +
                        "' out of range for type byte";
                    _unit->error(msg);
                    return false;
                }
                break;
            }
            case Builtin::KindShort:
            {
                IceUtil::Int64 l = IceUtilInternal::strToInt64(value.c_str(), 0, 0);
                if(l < Int16Min || l > Int16Max)
                {
                    string msg = "initializer `" + value + "' for " + desc + " `" + name +
                        "' out of range for type short";
                    _unit->error(msg);
                    return false;
                }
                break;
            }
            case Builtin::KindInt:
            {
                IceUtil::Int64 l = IceUtilInternal::strToInt64(value.c_str(), 0, 0);
                if(l < Int32Min || l > Int32Max)
                {
                    string msg = "initializer `" + value + "' for " + desc + " `" + name +
                        "' out of range for type int";
                    _unit->error(msg);
                    return false;
                }
                break;
            }

            default:
            {
                break;
            }
        }
    }

    if(e)
    {
        if(constant)
        {
            EnumPtr ec = EnumPtr::dynamicCast(constant->type());
            if(e != ec)
            {
                string msg = "type of initializer is incompatible with the type of " + desc + " `" + name + "'";
                _unit->error(msg);
                return false;
            }
        }
        else
        {
            if(valueType)
            {
                EnumeratorPtr lte = EnumeratorPtr::dynamicCast(valueType);

                if(!lte)
                {
                    string msg = "type of initializer is incompatible with the type of " + desc + " `" + name + "'";
                    _unit->error(msg);
                    return false;
                }
                EnumeratorList elist = e->enumerators();
                if(find(elist.begin(), elist.end(), lte) == elist.end())
                {
                    string msg = "enumerator `" + value + "' is not defined in enumeration `" + e->scoped() + "'";
                    _unit->error(msg);
                    return false;
                }
            }
            else
            {
                // Check if value designates an enumerator of e
                string newVal = value;
                string::size_type lastColon = value.rfind(':');
                if(lastColon != string::npos && lastColon + 1 < value.length())
                {
                    newVal = value.substr(0, lastColon + 1) + e->name() + "::" + value.substr(lastColon + 1);
                }

                ContainedList clist = e->lookupContained(newVal, false);
                if(clist.empty())
                {
                    string msg = "`" + value + "' does not designate an enumerator of `" + e->scoped() + "'";
                    _unit->error(msg);
                    return false;
                }
                EnumeratorPtr lte = EnumeratorPtr::dynamicCast(clist.front());
                if(lte)
                {
                    valueType = lte;
                    if(lastColon != string::npos)
                    {
                        _unit->warning(Deprecated, string("referencing enumerator `") + lte->name() +
                                       "' in its enumeration's enclosing scope is deprecated");
                    }
                }
                else
                {
                    string msg = "type of initializer is incompatible with the type of " + desc + " `" + name + "'";
                    _unit->error(msg);
                    return false;
                }
            }
        }
    }

    return true;
}

EnumeratorPtr
Slice::Container::validateEnumerator(const string& name)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        EnumeratorPtr p = EnumeratorPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() == name)
        {
            _unit->error(string("redefinition of enumerator `") + name + "'");
        }
        else
        {
            string msg = "enumerator `" + name + "' differs only in capitalization from ";
            msg += "`" + matches.front()->name() + "'";
            _unit->error(msg);
        }
    }

    nameIsLegal(name, "enumerator"); // Ignore return value.
    return 0;
}

// ----------------------------------------------------------------------
// Module
// ----------------------------------------------------------------------

Contained::ContainedType
Slice::Module::containedType() const
{
    return ContainedTypeModule;
}

bool
Slice::Module::uses(const ContainedPtr&) const
{
    return false;
}

string
Slice::Module::kindOf() const
{
    return "module";
}

void
Slice::Module::visit(ParserVisitor* visitor, bool all)
{
    if(visitor->visitModuleStart(this))
    {
        Container::visit(visitor, all);
        visitor->visitModuleEnd(this);
    }
}

Slice::Module::Module(const ContainerPtr& container, const string& name) :
    SyntaxTreeBase(container->unit()),
    Container(container->unit()),
    Contained(container, name)
{
}

// ----------------------------------------------------------------------
// Constructed
// ----------------------------------------------------------------------

bool
Slice::Constructed::isLocal() const
{
    return _local;
}

string
Slice::Constructed::typeId() const
{
    return scoped();
}

ConstructedList
Slice::Constructed::dependencies()
{
    set<ConstructedPtr> resultSet;
    recDependencies(resultSet);

#if defined(__SUNPRO_CC) && defined(_RWSTD_NO_MEMBER_TEMPLATES)
    // TODO: find a more usable work-around for this std lib limitation.
    ConstructedList result;
    set<ConstructedPtr>::iterator it = resultSet.begin();
    while(it != resultSet.end())
    {
        result.push_back(*it++);
    }
    return result;
#else
    return ConstructedList(resultSet.begin(), resultSet.end());
#endif
}

Slice::Constructed::Constructed(const ContainerPtr& container, const string& name, bool local) :
    SyntaxTreeBase(container->unit()),
    Type(container->unit()),
    Contained(container, name),
    _local(local)
{
}

// ----------------------------------------------------------------------
// ClassDecl
// ----------------------------------------------------------------------

void
Slice::ClassDecl::destroy()
{
    _definition = 0;
    SyntaxTreeBase::destroy();
}

ClassDefPtr
Slice::ClassDecl::definition() const
{
    return _definition;
}

bool
Slice::ClassDecl::isInterface() const
{
    return _interface;
}

Contained::ContainedType
Slice::ClassDecl::containedType() const
{
    return ContainedTypeClass;
}

bool
Slice::ClassDecl::uses(const ContainedPtr&) const
{
    return false;
}

bool
Slice::ClassDecl::usesClasses() const
{
    return true;
}

size_t
Slice::ClassDecl::minWireSize() const
{
    return 1; // At least four bytes for an instance, if the instance is marshaled as an index.
}

bool
Slice::ClassDecl::isVariableLength() const
{
    return true;
}

string
Slice::ClassDecl::kindOf() const
{
    string s;
    if(isLocal())
    {
        s += "local ";
    }
    s += _interface ? "interface" : "class";
    return s;
}

void
Slice::ClassDecl::visit(ParserVisitor* visitor, bool)
{
    visitor->visitClassDecl(this);
}

void
Slice::ClassDecl::recDependencies(set<ConstructedPtr>& dependencies)
{
    if(_definition)
    {
        _definition->containerRecDependencies(dependencies);
        ClassList bases = _definition->bases();
        for(ClassList::iterator p = bases.begin(); p != bases.end(); ++p)
        {
            (*p)->declaration()->recDependencies(dependencies);
        }
    }
}

void
Slice::ClassDecl::checkBasesAreLegal(const string& name, bool intf, bool local, const ClassList& bases,
                                     const UnitPtr& ut)
{
    //
    // Local definitions cannot have non-local bases, and vice versa.
    //
    for(ClassList::const_iterator p = bases.begin(); p != bases.end(); ++p)
    {
        if(local != (*p)->isLocal())
        {
            ostringstream msg;
            msg << (local ? "local" : "non-local") << " " << (intf ? "interface" : "class") << " `"
                << name << "' cannot have " << ((*p)->isLocal() ? "local" : "non-local") << " base "
                << ((*p)->isInterface() ? "interface" : "class") << " `" << (*p)->name() << "'";
            ut->error(msg.str());
        }
    }

    //
    // Check whether, for multiple inheritance, any of the bases define
    // the same operations.
    //
    if(bases.size() > 1)
    {
        //
        // We have multiple inheritance. Build a list of paths through the
        // inheritance graph, such that multiple inheritance is legal if
        // the union of the names defined in classes on each path are disjoint.
        //
        GraphPartitionList gpl;
        for(ClassList::const_iterator p = bases.begin(); p != bases.end(); ++p)
        {
            ClassList cl;
            gpl.push_back(cl);
            addPartition(gpl, gpl.rbegin(), *p);
        }

        //
        // We now have a list of partitions, with each partition containing
        // a list of class definitions. Turn the list of partitions of class
        // definitions into a list of sets of strings, with each
        // set containing the names of operations and data members defined in
        // the classes in each partition.
        //
        StringPartitionList spl = toStringPartitionList(gpl);

        //
        // Multiple inheritance is legal if no two partitions contain a common
        // name (that is, if the union of the intersections of all possible pairs
        // of partitions is empty).
        //
        checkPairIntersections(spl, name, ut);
    }
}

Slice::ClassDecl::ClassDecl(const ContainerPtr& container, const string& name, bool intf, bool local) :
    SyntaxTreeBase(container->unit()),
    Type(container->unit()),
    Contained(container, name),
    Constructed(container, name, local),
    _interface(intf)
{
    _unit->currentContainer();
}

//
// Return true if the class definition cdp is on one of the class lists in gpl, false otherwise.
//
bool
Slice::ClassDecl::isInList(const GraphPartitionList& gpl, const ClassDefPtr cdp)
{
    for(GraphPartitionList::const_iterator i = gpl.begin(); i != gpl.end(); ++i)
    {
        if(find(i->begin(), i->end(), cdp) != i->end())
        {
            return true;
        }
    }
    return false;
}

void
Slice::ClassDecl::addPartition(GraphPartitionList& gpl,
                               GraphPartitionList::reverse_iterator tail,
                               const ClassDefPtr base)
{
    //
    // If this base is on one of the partition lists already, do nothing.
    //
    if(isInList(gpl, base))
    {
        return;
    }
    //
    // Put the current base at the end of the current partition.
    //
    tail->push_back(base);
    //
    // If the base has bases in turn, recurse, adding the first base
    // of base (the left-most "grandbase") to the current partition.
    //
    if(base->bases().size())
    {
        addPartition(gpl, tail, *(base->bases().begin()));
    }
    //
    // If the base has multiple bases, each of the "grandbases"
    // except for the left-most (which we just dealt with)
    // adds a new partition.
    //
    if(base->bases().size() > 1)
    {
        ClassList grandBases = base->bases();
        ClassList::const_iterator i = grandBases.begin();
        while(++i != grandBases.end())
        {
            ClassList cl;
            gpl.push_back(cl);
            addPartition(gpl, gpl.rbegin(), *i);
        }
    }
}

//
// Convert the list of partitions of class definitions into a
// list of lists, with each member list containing the operation
// names defined by the interfaces in each partition.
//
Slice::ClassDecl::StringPartitionList
Slice::ClassDecl::toStringPartitionList(const GraphPartitionList& gpl)
{
    StringPartitionList spl;
    for(GraphPartitionList::const_iterator i = gpl.begin(); i != gpl.end(); ++i)
    {
        StringList sl;
        spl.push_back(sl);
        for(ClassList::const_iterator j = i->begin(); j != i->end(); ++j)
        {
            OperationList operations = (*j)->operations();
            for(OperationList::const_iterator l = operations.begin(); l != operations.end(); ++l)
            {
                spl.rbegin()->push_back((*l)->name());
            }
        }
    }
    return spl;
}

//
// For all (unique) pairs of string lists, check whether an identifier in one list occurs
// in the other and, if so, complain.
//
void
Slice::ClassDecl::checkPairIntersections(const StringPartitionList& l, const string& name, const UnitPtr& ut)
{
    set<string> reported;
    for(StringPartitionList::const_iterator i = l.begin(); i != l.end(); ++i)
    {
        StringPartitionList::const_iterator cursor = i;
        ++cursor;
        for(StringPartitionList::const_iterator j = cursor; j != l.end(); ++j)
        {
            for(StringList::const_iterator s1 = i->begin(); s1 != i->end(); ++s1)
            {
                for(StringList::const_iterator s2 = j->begin(); s2 != j->end(); ++s2)
                {
                    if((*s1) == (*s2) && reported.find(*s1) == reported.end())
                    {
                        string msg = "ambiguous multiple inheritance: `" + name;
                        msg += "' inherits operation `" + *s1 + "' from two or more unrelated base interfaces";
                        ut->error(msg);
                        reported.insert(*s1);
                    }
                    else if(!CICompare()(*s1, *s2) && !CICompare()(*s2, *s1) &&
                            reported.find(*s1) == reported.end() && reported.find(*s2) == reported.end())
                    {
                        string msg = "ambiguous multiple inheritance: `" + name;
                        msg += "' inherits operations `" + *s1 + "' and `" + *s2;
                        msg += "', which differ only in capitalization, from unrelated base interfaces";
                        ut->error(msg);
                        reported.insert(*s1);
                        reported.insert(*s2);
                    }
                }
            }
        }
    }
}

// ----------------------------------------------------------------------
// ClassDef
// ----------------------------------------------------------------------

void
Slice::ClassDef::destroy()
{
    _declaration = 0;
    _bases.clear();
    Container::destroy();
}

OperationPtr
Slice::ClassDef::createOperation(const string& name,
                                 const TypePtr& returnType,
                                 bool optional,
                                 int tag,
                                 Operation::Mode mode)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        OperationPtr p = OperationPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() != name)
        {
            string msg = "operation `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
        msg += "' as operation `" + name + "'";
        _unit->error(msg);
        return 0;
    }

    //
    // Check whether enclosing interface/class has the same name.
    //
    if(name == this->name())
    {
        string msg = isInterface() ? "interface" : "class";
        msg += " name `" + name + "' cannot be used as operation name";
        _unit->error(msg);
        return 0;
    }

    string newName = IceUtilInternal::toLower(name);
    string thisName = IceUtilInternal::toLower(this->name());
    if(newName == thisName)
    {
        string msg = "operation `" + name + "' differs only in capitalization from enclosing ";
        msg += isInterface() ? "interface" : "class";
        msg += " name `" + this->name() + "'";
        _unit->error(msg);
    }

    //
    // Check whether any bases have defined something with the same name already.
    //
    for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
    {
        ContainedList cl;
        OperationList ol = (*p)->allOperations();
        copy(ol.begin(), ol.end(), back_inserter(cl));
        DataMemberList dml = (*p)->allDataMembers();
        copy(dml.begin(), dml.end(), back_inserter(cl));
        for(ContainedList::const_iterator q = cl.begin(); q != cl.end(); ++q)
        {
            if((*q)->name() == name)
            {
                string msg = "operation `" + name;
                msg += "' is already defined as a";
                static const string vowels = "aeiou";
                string kindOf = (*q)->kindOf();
                if(vowels.find_first_of(kindOf[0]) != string::npos)
                {
                    msg += "n";
                }
                msg += " " + kindOf + " in a base interface or class";
                _unit->error(msg);
                return 0;
            }

            string baseName = IceUtilInternal::toLower((*q)->name());
            string newName2 = IceUtilInternal::toLower(name);
            if(baseName == newName2)
            {
                string msg = "operation `" + name + "' differs only in capitalization from " + (*q)->kindOf();
                msg += " `" + (*q)->name() + "', which is defined in a base interface or class";
                _unit->error(msg);
            }
        }
    }

    //
    // Non-local class/interface cannot have operation with local return type.
    //
    if(!isLocal() && returnType && returnType->isLocal())
    {
        string msg = "non-local " + this->kindOf() + " `" + this->name() + "' cannot have operation `";
        msg += name + "' with local return type";
        _unit->error(msg);
    }

    if(!isInterface() && !isLocal() && !_hasOperations)
    {
        // Only warn for the first operation
        _unit->warning(Deprecated, "classes with operations are deprecated");
    }

    _hasOperations = true;
    OperationPtr op = new Operation(this, name, returnType, optional, tag, mode);
    _contents.push_back(op);
    return op;
}

DataMemberPtr
Slice::ClassDef::createDataMember(const string& name, const TypePtr& type, bool optional, int tag,
                                  const SyntaxTreeBasePtr& defaultValueType, const string& defaultValue,
                                  const string& defaultLiteral)
{
    assert(!isInterface());
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        DataMemberPtr p = DataMemberPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }

        if(matches.front()->name() != name)
        {
            string msg = "data member `" + name + "' differs only in capitalization from ";
            msg += matches.front()->kindOf() + " `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        else
        {
            string msg = "redefinition of " + matches.front()->kindOf() + " `" + matches.front()->name();
            msg += "' as data member `" + name + "'";
            _unit->error(msg);
            return 0;
        }
    }

    string newName = IceUtilInternal::toLower(name);

    //
    // Check whether any bases have defined something with the same name already.
    //
    for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
    {
        ContainedList cl;
        OperationList ol = (*p)->allOperations();
        copy(ol.begin(), ol.end(), back_inserter(cl));
        DataMemberList dml = (*p)->allDataMembers();
        copy(dml.begin(), dml.end(), back_inserter(cl));
        for(ContainedList::const_iterator q = cl.begin(); q != cl.end(); ++q)
        {
            if((*q)->name() == name)
            {
                string msg = "data member `" + name;
                msg += "' is already defined as a";
                static const string vowels = "aeiou";
                string kindOf = (*q)->kindOf();
                if(vowels.find_first_of(kindOf[0]) != string::npos)
                {
                    msg += "n";
                }
                msg += " " + kindOf + " in a base interface or class";
                _unit->error(msg);
                return 0;
            }

            string baseName = IceUtilInternal::toLower((*q)->name());
            string newName2 = IceUtilInternal::toLower(name);
            if(baseName == newName2)
            {
                string msg = "data member `" + name + "' differs only in capitalization from " + (*q)->kindOf();
                msg += " `" + (*q)->name() + "', which is defined in a base interface or class";
                _unit->error(msg);
            }
        }
    }

    //
    // If data member is local, enclosing class/interface must be local.
    //
    if(!isLocal() && type->isLocal())
    {
        string msg = "non-local " + kindOf() + "`" + this->name() + "' cannot contain local member `" + name + "'";
        _unit->error(msg);
    }

    SyntaxTreeBasePtr dlt = defaultValueType;
    string dv = defaultValue;
    string dl = defaultLiteral;

    if(dlt || (EnumPtr::dynamicCast(type) && !dv.empty()))
    {
        //
        // Validate the default value.
        //
        if(!validateConstant(name, type, dlt, dv, false))
        {
            //
            // Create the data member anyway, just without the default value.
            //
            dlt = 0;
            dv.clear();
            dl.clear();
        }
    }

    if(optional)
    {
        //
        // Validate the tag.
        //
        DataMemberList dml = dataMembers();
        for(DataMemberList::iterator q = dml.begin(); q != dml.end(); ++q)
        {
            if((*q)->optional() && tag == (*q)->tag())
            {
                string msg = "tag for optional data member `" + name + "' is already in use";
                _unit->error(msg);
                break;
            }
        }
    }

    checkDeprecatedType(_unit, type);

    _hasDataMembers = true;
    DataMemberPtr member = new DataMember(this, name, type, optional, tag, dlt, dv, dl);
    _contents.push_back(member);
    return member;
}

ClassDeclPtr
Slice::ClassDef::declaration() const
{
    return _declaration;
}

ClassList
Slice::ClassDef::bases() const
{
    return _bases;
}

ClassList
Slice::ClassDef::allBases() const
{
    ClassList result = _bases;
    result.sort();
    result.unique();
    for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
    {
        ClassList li = (*p)->allBases();
        result.merge(li);
        result.unique();
    }
    return result;
}

OperationList
Slice::ClassDef::operations() const
{
    OperationList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        OperationPtr q = OperationPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

OperationList
Slice::ClassDef::allOperations() const
{
    OperationList result;
    for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
    {
        OperationList li = (*p)->allOperations();
        for(OperationList::const_iterator q = li.begin(); q != li.end(); ++q)
        {
            if(find(result.begin(), result.end(), *q) == result.end())
            {
                result.push_back(*q);
            }
        }
    }

    OperationList li = operations();
    for(OperationList::const_iterator q = li.begin(); q != li.end(); ++q)
    {
        if(find(result.begin(), result.end(), *q) == result.end())
        {
            result.push_back(*q);
        }
    }
    return result;
}

DataMemberList
Slice::ClassDef::dataMembers() const
{
    DataMemberList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DataMemberPtr q = DataMemberPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

DataMemberList
Slice::ClassDef::orderedOptionalDataMembers() const
{
    return filterOrderedOptionalDataMembers(dataMembers());
}

//
// Return the data members of this class and its parent classes, in base-to-derived order.
//
DataMemberList
Slice::ClassDef::allDataMembers() const
{
    DataMemberList result;

    //
    // Check if we have a base class. If so, recursively
    // get the data members of the base(s).
    //
    if(!_bases.empty() && !_bases.front()->isInterface())
    {
        result = _bases.front()->allDataMembers();
    }

    //
    // Append this class's data members.
    //
    DataMemberList myMembers = dataMembers();
    result.splice(result.end(), myMembers);

    return result;
}

DataMemberList
Slice::ClassDef::classDataMembers() const
{
    DataMemberList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DataMemberPtr q = DataMemberPtr::dynamicCast(*p);
        if(q)
        {
            BuiltinPtr builtin = BuiltinPtr::dynamicCast(q->type());
            if((builtin && builtin->kind() == Builtin::KindObject) ||
               (builtin && builtin->kind() == Builtin::KindValue) ||
               ClassDeclPtr::dynamicCast(q->type()))
            {
                result.push_back(q);
            }
        }
    }
    return result;
}

//
// Return the class data members of this class and its parent classes, in base-to-derived order.
//
DataMemberList
Slice::ClassDef::allClassDataMembers() const
{
    DataMemberList result;

    //
    // Check if we have a base class. If so, recursively
    // get the class data members of the base(s).
    //
    if(!_bases.empty() && !_bases.front()->isInterface())
    {
        result = _bases.front()->allClassDataMembers();
    }

    //
    // Append this class's class members.
    //
    DataMemberList myMembers = classDataMembers();
    result.splice(result.end(), myMembers);

    return result;
}

bool
Slice::ClassDef::canBeCyclic() const
{
    if(!_bases.empty() && !_bases.front()->isInterface() && _bases.front()->canBeCyclic())
    {
        return true;
    }
    DataMemberList dml = dataMembers();
    for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i)
    {
        if((*i)->type()->usesClasses())
        {
            return true;
        }
    }
    return false;
}

bool
Slice::ClassDef::isAbstract() const
{
    if(isInterface() || _bases.size() > 1) // Is this an interface, or does it derive from interfaces?
    {
        return true;
    }

    if(!_bases.empty() && _bases.front()->isAbstract())
    {
        return true;
    }

    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        if(OperationPtr::dynamicCast(*p))
        {
            return true;
        }
    }

    return false;
}

bool
Slice::ClassDef::isInterface() const
{
    return _interface;
}

bool
Slice::ClassDef::isA(const string& id) const
{
    if(id == _scoped)
    {
        return true;
    }
    for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
    {
        if((*p)->isA(id))
        {
            return true;
        }
    }
    return false;
}

bool
Slice::ClassDef::isLocal() const
{
    return _local;
}

bool
Slice::ClassDef::hasDataMembers() const
{
    return _hasDataMembers;
}

bool
Slice::ClassDef::hasOperations() const
{
    return _hasOperations;
}

bool
Slice::ClassDef::hasDefaultValues() const
{
    DataMemberList dml = dataMembers();
    for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i)
    {
        if((*i)->defaultValueType())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::ClassDef::inheritsMetaData(const string& meta) const
{
    for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
    {
        if((*p)->hasMetaData(meta) || (*p)->inheritsMetaData(meta))
        {
            return true;
        }
    }

    return false;
}

bool
Slice::ClassDef::hasBaseDataMembers() const
{
    if(!_bases.empty() && !_bases.front()->isInterface())
    {
        return !_bases.front()->allDataMembers().empty();
    }

    return false;
}

Contained::ContainedType
Slice::ClassDef::containedType() const
{
    return ContainedTypeClass;
}

bool
Slice::ClassDef::uses(const ContainedPtr&) const
{
    // No uses() implementation here. DataMember and Operation have
    // their own uses().
    return false;
}

string
Slice::ClassDef::kindOf() const
{
    string s;
    if(isLocal())
    {
        s += "local ";
    }
    s += isInterface() ? "interface" : "class";
    return s;
}

void
Slice::ClassDef::visit(ParserVisitor* visitor, bool all)
{
    if(visitor->visitClassDefStart(this))
    {
        Container::visit(visitor, all);
        visitor->visitClassDefEnd(this);
    }
}

int
Slice::ClassDef::compactId() const
{
    return _compactId;
}

bool
Slice::ClassDef::isDelegate() const
{
    return isLocal() && isInterface() && hasMetaData("delegate") && allOperations().size() == 1;
}

Slice::ClassDef::ClassDef(const ContainerPtr& container, const string& name, int id, bool intf, const ClassList& bases,
                          bool local) :
    SyntaxTreeBase(container->unit()),
    Container(container->unit()),
    Contained(container, name),
    _interface(intf),
    _hasDataMembers(false),
    _hasOperations(false),
    _bases(bases),
    _local(local),
    _compactId(id)
{
    if(!local && !intf)
    {
        for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
        {
            if((*p)->isInterface())
            {
                _unit->warning(Deprecated, "classes implementing interfaces are deprecated");
                break;
            }
        }
    }
    //
    // First element of bases may be a class, all others must be
    // interfaces.
    //
#ifndef NDEBUG
    for(ClassList::const_iterator p = _bases.begin(); p != _bases.end(); ++p)
    {
        assert(p == _bases.begin() || (*p)->isInterface());
    }
#endif

    if(_compactId >= 0)
    {
        _unit->addTypeId(_compactId, scoped());
    }
}

// ----------------------------------------------------------------------
// Proxy
// ----------------------------------------------------------------------

bool
Slice::Proxy::isLocal() const
{
    return _classDecl->isLocal();
}

string
Slice::Proxy::typeId() const
{
    return _classDecl->scoped();
}

bool
Slice::Proxy::usesClasses() const
{
    return false;
}

size_t
Slice::Proxy::minWireSize() const
{
    return 2; // At least two bytes for a nil proxy (empty name and empty category strings).
}

bool
Slice::Proxy::isVariableLength() const
{
    return true;
}

ClassDeclPtr
Slice::Proxy::_class() const
{
    return _classDecl;
}

Slice::Proxy::Proxy(const ClassDeclPtr& cl) :
    SyntaxTreeBase(cl->unit(), cl->definitionContext()),
    Type(cl->unit()),
    _classDecl(cl)
{
}

// ----------------------------------------------------------------------
// Exception
// ----------------------------------------------------------------------

void
Slice::Exception::destroy()
{
    _base = 0;
    Container::destroy();
}

DataMemberPtr
Slice::Exception::createDataMember(const string& name, const TypePtr& type, bool optional, int tag,
                                   const SyntaxTreeBasePtr& defaultValueType, const string& defaultValue,
                                   const string& defaultLiteral)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        DataMemberPtr p = DataMemberPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() != name)
        {
            string msg = "exception member `" + name + "' differs only in capitalization from ";
            msg += "exception member `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        else
        {
            string msg = "redefinition of exception member `" + name + "'";
            _unit->error(msg);
            return 0;
        }
    }

    string newName = IceUtilInternal::toLower(name);
    //
    // Check whether any bases have defined a member with the same name already.
    //
    ExceptionList bl = allBases();
    for(ExceptionList::const_iterator q = bl.begin(); q != bl.end(); ++q)
    {
        ContainedList cl;
        DataMemberList dml = (*q)->dataMembers();
        copy(dml.begin(), dml.end(), back_inserter(cl));
        for(ContainedList::const_iterator r = cl.begin(); r != cl.end(); ++r)
        {
            if((*r)->name() == name)
            {
                string msg = "exception member `" + name + "' is already defined in a base exception";
                _unit->error(msg);
                return 0;
            }

            string baseName = IceUtilInternal::toLower((*r)->name());
            string newName2 = IceUtilInternal::toLower(name);
            if(baseName == newName2)
            {
                string msg = "exception member `" + name + "' differs only in capitalization from exception member `";
                msg += (*r)->name() + "', which is defined in a base exception";
                _unit->error(msg);
            }
        }
    }

    //
    // If data member is local, enclosing class/interface must be local.
    //
    if(!isLocal() && type->isLocal())
    {
        string msg = "non-local " + kindOf() + "`" + this->name() + "' cannot contain local member `" + name + "'";
        _unit->error(msg);
    }

    SyntaxTreeBasePtr dlt = defaultValueType;
    string dv = defaultValue;
    string dl = defaultLiteral;

    if(dlt || (EnumPtr::dynamicCast(type) && !dv.empty()))
    {
        //
        // Validate the default value.
        //
        if(!validateConstant(name, type, dlt, dv, false))
        {
            //
            // Create the data member anyway, just without the default value.
            //
            dlt = 0;
            dv.clear();
            dl.clear();
        }
    }

    if(optional)
    {
        //
        // Validate the tag.
        //
        DataMemberList dml = dataMembers();
        for(DataMemberList::iterator q = dml.begin(); q != dml.end(); ++q)
        {
            if((*q)->optional() && tag == (*q)->tag())
            {
                string msg = "tag for optional data member `" + name + "' is already in use";
                _unit->error(msg);
                break;
            }
        }
    }

    checkDeprecatedType(_unit, type);

    DataMemberPtr p = new DataMember(this, name, type, optional, tag, dlt, dv, dl);
    _contents.push_back(p);
    return p;
}

DataMemberList
Slice::Exception::dataMembers() const
{
    DataMemberList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DataMemberPtr q = DataMemberPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

DataMemberList
Slice::Exception::orderedOptionalDataMembers() const
{
    return filterOrderedOptionalDataMembers(dataMembers());
}

//
// Return the data members of this exception and its parent exceptions, in base-to-derived order.
//
DataMemberList
Slice::Exception::allDataMembers() const
{
    DataMemberList result;

    //
    // Check if we have a base exception. If so, recursively
    // get the data members of the base exception(s).
    //
    if(base())
    {
        result = base()->allDataMembers();
    }

    //
    // Append this exceptions's data members.
    //
    DataMemberList myMembers = dataMembers();
    result.splice(result.end(), myMembers);

    return result;
}

DataMemberList
Slice::Exception::classDataMembers() const
{
    DataMemberList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DataMemberPtr q = DataMemberPtr::dynamicCast(*p);
        if(q)
        {
            BuiltinPtr builtin = BuiltinPtr::dynamicCast(q->type());
            if((builtin && builtin->kind() == Builtin::KindObject) ||
               (builtin && builtin->kind() == Builtin::KindValue) ||
               ClassDeclPtr::dynamicCast(q->type()))
            {
                result.push_back(q);
            }
        }
    }
    return result;
}

//
// Return the class data members of this exception and its parent exceptions, in base-to-derived order.
//
DataMemberList
Slice::Exception::allClassDataMembers() const
{
    DataMemberList result;

    //
    // Check if we have a base exception. If so, recursively
    // get the class data members of the base exception(s).
    //
    if(base())
    {
        result = base()->allClassDataMembers();
    }

    //
    // Append this exceptions's class data members.
    //
    DataMemberList myMembers = classDataMembers();
    result.splice(result.end(), myMembers);

    return result;
}

ExceptionPtr
Slice::Exception::base() const
{
    return _base;
}

ExceptionList
Slice::Exception::allBases() const
{
    ExceptionList result;
    if(_base)
    {
        result = _base->allBases();
        result.push_front(_base);
    }
    return result;
}

bool
Slice::Exception::isBaseOf(const ExceptionPtr& other) const
{
    if(this->scoped() == other->scoped())
    {
        return false;
    }
    ExceptionList bases = other->allBases();
    for(ExceptionList::const_iterator i = bases.begin(); i != bases.end(); ++i)
    {
        if((*i)->scoped() == scoped())
        {
            return true;
        }
    }
    return false;
}

bool
Slice::Exception::isLocal() const
{
    return _local;
}

Contained::ContainedType
Slice::Exception::containedType() const
{
    return ContainedTypeException;
}

bool
Slice::Exception::uses(const ContainedPtr&) const
{
    // No uses() implementation here. DataMember has its own uses().
    return false;
}

bool
Slice::Exception::usesClasses(bool includeOptional) const
{
    DataMemberList dml = dataMembers();
    for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i)
    {
        if((*i)->type()->usesClasses() && (includeOptional || !(*i)->optional()))
        {
            return true;
        }
    }
    if(_base)
    {
        return _base->usesClasses(includeOptional);
    }
    return false;
}

bool
Slice::Exception::hasDefaultValues() const
{
    DataMemberList dml = dataMembers();
    for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i)
    {
        if((*i)->defaultValueType())
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Exception::inheritsMetaData(const string& meta) const
{
    if(_base && (_base->hasMetaData(meta) || _base->inheritsMetaData(meta)))
    {
        return true;
    }

    return false;
}

bool
Slice::Exception::hasBaseDataMembers() const
{
    return _base && !_base->allDataMembers().empty();
}

string
Slice::Exception::kindOf() const
{
    return "exception";
}

void
Slice::Exception::visit(ParserVisitor* visitor, bool all)
{
    if(visitor->visitExceptionStart(this))
    {
        Container::visit(visitor, all);
        visitor->visitExceptionEnd(this);
    }
}

Slice::Exception::Exception(const ContainerPtr& container, const string& name, const ExceptionPtr& base, bool local) :
    SyntaxTreeBase(container->unit()),
    Container(container->unit()),
    Contained(container, name),
    _base(base),
    _local(local)
{
}

// ----------------------------------------------------------------------
// Struct
// ----------------------------------------------------------------------

DataMemberPtr
Slice::Struct::createDataMember(const string& name, const TypePtr& type, bool optional, int tag,
                                const SyntaxTreeBasePtr& defaultValueType, const string& defaultValue,
                                const string& defaultLiteral)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        DataMemberPtr p = DataMemberPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() != name)
        {
            string msg = "member `" + name + "' differs only in capitalization from ";
            msg += "member `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        else
        {
            string msg = "redefinition of struct member `" + name + "'";
            _unit->error(msg);
            return 0;
        }
    }

    //
    // Structures cannot contain themselves.
    //
    if(type.get() == this)
    {
        string msg = "struct `";
        msg += this->name();
        msg += "' cannot contain itself";
        _unit->error(msg);
        return 0;
    }

    //
    // If data member is local, enclosing class/interface must be local.
    //
    if(!isLocal() && type->isLocal())
    {
        string msg = "non-local " + kindOf() + "`" + this->name() + "' cannot contain local member `" + name + "'";
        _unit->error(msg);
    }

    SyntaxTreeBasePtr dlt = defaultValueType;
    string dv = defaultValue;
    string dl = defaultLiteral;

    if(dlt || (EnumPtr::dynamicCast(type) && !dv.empty()))
    {
        //
        // Validate the default value.
        //
        if(!validateConstant(name, type, dlt, dv, false))
        {
            //
            // Create the data member anyway, just without the default value.
            //
            dlt = 0;
            dv.clear();
            dl.clear();
        }
    }

    if(optional)
    {
        //
        // Validate the tag.
        //
        DataMemberList dml = dataMembers();
        for(DataMemberList::iterator q = dml.begin(); q != dml.end(); ++q)
        {
            if((*q)->optional() && tag == (*q)->tag())
            {
                string msg = "tag for optional data member `" + name + "' is already in use";
                _unit->error(msg);
                break;
            }
        }
    }

    checkDeprecatedType(_unit, type);

    DataMemberPtr p = new DataMember(this, name, type, optional, tag, dlt, dv, dl);
    _contents.push_back(p);
    return p;
}

DataMemberList
Slice::Struct::dataMembers() const
{
    DataMemberList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DataMemberPtr q = DataMemberPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

DataMemberList
Slice::Struct::classDataMembers() const
{
    DataMemberList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DataMemberPtr q = DataMemberPtr::dynamicCast(*p);
        if(q)
        {
            BuiltinPtr builtin = BuiltinPtr::dynamicCast(q->type());
            if((builtin && builtin->kind() == Builtin::KindObject) ||
               (builtin && builtin->kind() == Builtin::KindValue) ||
               ClassDeclPtr::dynamicCast(q->type()))
            {
                result.push_back(q);
            }
        }
    }
    return result;
}

Contained::ContainedType
Slice::Struct::containedType() const
{
    return ContainedTypeStruct;
}

bool
Slice::Struct::uses(const ContainedPtr&) const
{
    return false;
}

bool
Slice::Struct::usesClasses() const
{
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        DataMemberPtr q = DataMemberPtr::dynamicCast(*p);
        if(q)
        {
            TypePtr t = q->type();
            if(t->usesClasses())
            {
                return true;
            }
        }
    }
    return false;
}

size_t
Slice::Struct::minWireSize() const
{
    //
    // At least the sum of the minimum member sizes.
    //
    size_t sz = 0;
    DataMemberList dml = dataMembers();
    for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i)
    {
        sz += (*i)->type()->minWireSize();
    }
    return sz;
}

bool
Slice::Struct::isVariableLength() const
{
    DataMemberList dml = dataMembers();
    for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i)
    {
        if((*i)->type()->isVariableLength())
        {
            return true;
        }
    }
    return false;
}

bool
Slice::Struct::hasDefaultValues() const
{
    DataMemberList dml = dataMembers();
    for(DataMemberList::const_iterator i = dml.begin(); i != dml.end(); ++i)
    {
        if((*i)->defaultValueType())
        {
            return true;
        }
    }
    return false;
}

string
Slice::Struct::kindOf() const
{
    return "struct";
}

void
Slice::Struct::visit(ParserVisitor* visitor, bool all)
{
    if(visitor->visitStructStart(this))
    {
        Container::visit(visitor, all);
        visitor->visitStructEnd(this);
    }
}

void
Slice::Struct::recDependencies(set<ConstructedPtr>& dependencies)
{
    containerRecDependencies(dependencies);
}

Slice::Struct::Struct(const ContainerPtr& container, const string& name, bool local) :
    SyntaxTreeBase(container->unit()),
    Container(container->unit()),
    Type(container->unit()),
    Contained(container, name),
    Constructed(container, name, local)
{
}

// ----------------------------------------------------------------------
// Sequence
// ----------------------------------------------------------------------

TypePtr
Slice::Sequence::type() const
{
    return _type;
}

StringList
Slice::Sequence::typeMetaData() const
{
    return _typeMetaData;
}

Contained::ContainedType
Slice::Sequence::containedType() const
{
    return ContainedTypeSequence;
}

bool
Slice::Sequence::uses(const ContainedPtr& contained) const
{
    ContainedPtr contained2 = ContainedPtr::dynamicCast(_type);
    if(contained2 && contained2 == contained)
    {
        return true;
    }

    return false;
}

bool
Slice::Sequence::usesClasses() const
{
    return _type->usesClasses();
}

size_t
Slice::Sequence::minWireSize() const
{
    return 1; // An empty sequence.
}

bool
Slice::Sequence::isVariableLength() const
{
    return true;
}

string
Slice::Sequence::kindOf() const
{
    return "sequence";
}

void
Slice::Sequence::visit(ParserVisitor* visitor, bool)
{
    visitor->visitSequence(this);
}

void
Slice::Sequence::recDependencies(set<ConstructedPtr>& dependencies)
{
    ConstructedPtr constructed = ConstructedPtr::dynamicCast(_type);
    if(constructed && dependencies.find(constructed) != dependencies.end())
    {
        dependencies.insert(constructed);
        constructed->recDependencies(dependencies);
    }
}

Slice::Sequence::Sequence(const ContainerPtr& container, const string& name, const TypePtr& type,
                          const StringList& typeMetaData, bool local) :
    SyntaxTreeBase(container->unit()),
    Type(container->unit()),
    Contained(container, name),
    Constructed(container, name, local),
    _type(type),
    _typeMetaData(typeMetaData)
{
}

// ----------------------------------------------------------------------
// Dictionary
// ----------------------------------------------------------------------

TypePtr
Slice::Dictionary::keyType() const
{
    return _keyType;
}

TypePtr
Slice::Dictionary::valueType() const
{
    return _valueType;
}

StringList
Slice::Dictionary::keyMetaData() const
{
    return _keyMetaData;
}

StringList
Slice::Dictionary::valueMetaData() const
{
    return _valueMetaData;
}

Contained::ContainedType
Slice::Dictionary::containedType() const
{
    return ContainedTypeDictionary;
}

bool
Slice::Dictionary::uses(const ContainedPtr& contained) const
{
    {
        ContainedPtr contained2 = ContainedPtr::dynamicCast(_keyType);
        if(contained2 && contained2 == contained)
        {
            return true;
        }
    }

    {
        ContainedPtr contained2 = ContainedPtr::dynamicCast(_valueType);
        if(contained2 && contained2 == contained)
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Dictionary::usesClasses() const
{
    return _valueType->usesClasses();
}

size_t
Slice::Dictionary::minWireSize() const
{
    return 1; // An empty dictionary.
}

bool
Slice::Dictionary::isVariableLength() const
{
    return true;
}

string
Slice::Dictionary::kindOf() const
{
    return "dictionary";
}

void
Slice::Dictionary::visit(ParserVisitor* visitor, bool)
{
    visitor->visitDictionary(this);
}

void
Slice::Dictionary::recDependencies(set<ConstructedPtr>& dependencies)
{
    {
        ConstructedPtr constructed = ConstructedPtr::dynamicCast(_keyType);
        if(constructed && dependencies.find(constructed) != dependencies.end())
        {
            dependencies.insert(constructed);
            constructed->recDependencies(dependencies);
        }
    }

    {
        ConstructedPtr constructed = ConstructedPtr::dynamicCast(_valueType);
        if(constructed && dependencies.find(constructed) != dependencies.end())
        {
            dependencies.insert(constructed);
            constructed->recDependencies(dependencies);
        }
    }
}

//
// Check that the key type of a dictionary is legal. Legal types are
// integral types, string, and sequences and structs containing only
// other legal key types.
//
// Note: Allowing sequences in dictionary keys has been deprecated as
//       of Ice 3.3.0.
//
bool
Slice::Dictionary::legalKeyType(const TypePtr& type, bool& containsSequence)
{
    BuiltinPtr bp = BuiltinPtr::dynamicCast(type);
    if(bp)
    {
        switch(bp->kind())
        {
            case Builtin::KindByte:
            case Builtin::KindBool:
            case Builtin::KindShort:
            case Builtin::KindInt:
            case Builtin::KindLong:
            case Builtin::KindString:
            {
                return true;
                break;
            }

            case Builtin::KindFloat:
            case Builtin::KindDouble:
            case Builtin::KindObject:
            case Builtin::KindObjectProxy:
            case Builtin::KindLocalObject:
            case Builtin::KindValue:
            {
                return false;
                break;
            }
        }
    }

    EnumPtr ep = EnumPtr::dynamicCast(type);
    if(ep)
    {
        return true;
    }

    SequencePtr seqp = SequencePtr::dynamicCast(type);
    if(seqp)
    {
        containsSequence = true;
        if(legalKeyType(seqp->type(), containsSequence))
        {
            return true;
        }
    }

    StructPtr strp = StructPtr::dynamicCast(type);
    if(strp)
    {
        DataMemberList dml = strp->dataMembers();
        for(DataMemberList::const_iterator mem = dml.begin(); mem != dml.end(); ++mem)
        {
            if(!legalKeyType((*mem)->type(), containsSequence))
            {
                return false;
            }
        }
        return true;
    }

    return false;
}

Slice::Dictionary::Dictionary(const ContainerPtr& container, const string& name, const TypePtr& keyType,
                              const StringList& keyMetaData, const TypePtr& valueType,
                              const StringList& valueMetaData, bool local) :
    SyntaxTreeBase(container->unit()),
    Type(container->unit()),
    Contained(container, name),
    Constructed(container, name, local),
    _keyType(keyType),
    _valueType(valueType),
    _keyMetaData(keyMetaData),
    _valueMetaData(valueMetaData)
{
}

// ----------------------------------------------------------------------
// Enum
// ----------------------------------------------------------------------

void
Slice::Enum::destroy()
{
    SyntaxTreeBase::destroy();
}

bool
Slice::Enum::explicitValue() const
{
    return _explicitValue;
}

int
Slice::Enum::minValue() const
{
    return static_cast<int>(_minValue);
}

int
Slice::Enum::maxValue() const
{
    return static_cast<int>(_maxValue);
}

Contained::ContainedType
Slice::Enum::containedType() const
{
    return ContainedTypeEnum;
}

bool
Slice::Enum::uses(const ContainedPtr&) const
{
    return false;
}

bool
Slice::Enum::usesClasses() const
{
    return false;
}

size_t
Slice::Enum::minWireSize() const
{
    return 1;
}

bool
Slice::Enum::isVariableLength() const
{
    return true;
}

string
Slice::Enum::kindOf() const
{
    return "enumeration";
}

void
Slice::Enum::visit(ParserVisitor* visitor, bool)
{
    visitor->visitEnum(this);
}

void
Slice::Enum::recDependencies(set<ConstructedPtr>&)
{
    // An Enum does not have any dependencies.
}

Slice::Enum::Enum(const ContainerPtr& container, const string& name, bool local) :
    SyntaxTreeBase(container->unit()),
    Container(container->unit()),
    Type(container->unit()),
    Contained(container, name),
    Constructed(container, name, local),
    _explicitValue(false),
    _minValue(Int32Max),
    _maxValue(0),
    _lastValue(-1)
{
}

int
Slice::Enum::newEnumerator(const EnumeratorPtr& p)
{
    if(p->explicitValue())
    {
        _explicitValue = true;
        _lastValue = p->value();

        if(_lastValue < 0)
        {
            string msg = "value for enumerator `" + p->name() + "' is out of range";
            _unit->error(msg);
        }
    }
    else
    {
        if(_lastValue == Int32Max)
        {
            string msg = "value for enumerator `" + p->name() + "' is out of range";
            _unit->error(msg);
        }
        else
        {
            //
            // If the enumerator was not assigned an explicit value, we automatically assign
            // it one more than the previous enumerator.
            //
            ++_lastValue;
        }
    }

    bool checkForDuplicates = true;
    if(_lastValue > _maxValue)
    {
        _maxValue = _lastValue;
        checkForDuplicates = false;
    }
    if(_lastValue < _minValue)
    {
        _minValue = _lastValue;
        checkForDuplicates = false;
    }

    if(checkForDuplicates)
    {
        EnumeratorList enl = enumerators();
        for(EnumeratorList::iterator q = enl.begin(); q != enl.end(); ++q)
        {
            EnumeratorPtr& r = *q;
            if(r != p && r->value() == _lastValue)
            {
                _unit->error(string("enumerator `") + p->name() + "' has the same value as enumerator `" +
                             r->name() + "'");
            }
        }
    }

    return _lastValue;
}

// ----------------------------------------------------------------------
// Enumerator
// ----------------------------------------------------------------------

EnumPtr
Slice::Enumerator::type() const
{
    return EnumPtr::dynamicCast(container());
}

Contained::ContainedType
Slice::Enumerator::containedType() const
{
    return ContainedTypeEnumerator;
}

bool
Slice::Enumerator::uses(const ContainedPtr&) const
{
    return false;
}

string
Slice::Enumerator::kindOf() const
{
    return "enumerator";
}

bool
Slice::Enumerator::explicitValue() const
{
    return _explicitValue;
}

int
Slice::Enumerator::value() const
{
    return _value;
}

Slice::Enumerator::Enumerator(const ContainerPtr& container, const string& name) :
    SyntaxTreeBase(container->unit()),
    Contained(container, name),
    _explicitValue(false),
    _value(-1)
{
    _value = EnumPtr::dynamicCast(container)->newEnumerator(this);
}

Slice::Enumerator::Enumerator(const ContainerPtr& container, const string& name, int value) :
    SyntaxTreeBase(container->unit()),
    Contained(container, name),
    _explicitValue(true),
    _value(value)
{
    EnumPtr::dynamicCast(container)->newEnumerator(this);
}

// ----------------------------------------------------------------------
// Const
// ----------------------------------------------------------------------

TypePtr
Slice::Const::type() const
{
    return _type;
}

StringList
Slice::Const::typeMetaData() const
{
    return _typeMetaData;
}

SyntaxTreeBasePtr
Slice::Const::valueType() const
{
    return _valueType;
}

string
Slice::Const::value() const
{
    return _value;
}

string
Slice::Const::literal() const
{
    return _literal;
}

Contained::ContainedType
Slice::Const::containedType() const
{
    return ContainedTypeConstant;
}

bool
Slice::Const::uses(const ContainedPtr& contained) const
{
    ContainedPtr contained2 = ContainedPtr::dynamicCast(_type);
    return (contained2 && contained2 == contained);
}

string
Slice::Const::kindOf() const
{
    return "constant";
}

void
Slice::Const::visit(ParserVisitor* visitor, bool)
{
    visitor->visitConst(this);
}

Slice::Const::Const(const ContainerPtr& container, const string& name, const TypePtr& type,
                    const StringList& typeMetaData, const SyntaxTreeBasePtr& valueType, const string& value,
                    const string& literal) :
    SyntaxTreeBase(container->unit()),
    Contained(container, name),
    _type(type),
    _typeMetaData(typeMetaData),
    _valueType(valueType),
    _value(value),
    _literal(literal)
{
    if(valueType == 0)
    {
        cerr << "const " << name << " created with null valueType" << endl;
    }

}

// ----------------------------------------------------------------------
// Operation
// ----------------------------------------------------------------------

TypePtr
Slice::Operation::returnType() const
{
    return _returnType;
}

bool
Slice::Operation::returnIsOptional() const
{
    return _returnIsOptional;
}

int
Slice::Operation::returnTag() const
{
    return _returnTag;
}

Operation::Mode
Slice::Operation::mode() const
{
    return _mode;
}

Operation::Mode
Slice::Operation::sendMode() const
{
    if(_mode == Operation::Idempotent && hasMetaData("nonmutating"))
    {
        return Operation::Nonmutating;
    }
    else
    {
        return _mode;
    }
}

bool
Slice::Operation::hasMarshaledResult() const
{
    ClassDefPtr cl = ClassDefPtr::dynamicCast(container());
    assert(cl);
    if(cl->hasMetaData("marshaled-result") || hasMetaData("marshaled-result"))
    {
        if(returnType() && isMutableAfterReturnType(returnType()))
        {
            return true;
        }
        for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
        {
            ParamDeclPtr q = ParamDeclPtr::dynamicCast(*p);
            if(q->isOutParam() && isMutableAfterReturnType(q->type()))
            {
                return true;
            }
        }
    }
    return false;
}

ParamDeclPtr
Slice::Operation::createParamDecl(const string& name, const TypePtr& type, bool isOutParam, bool optional, int tag)
{
    ContainedList matches = _unit->findContents(thisScope() + name);
    if(!matches.empty())
    {
        ParamDeclPtr p = ParamDeclPtr::dynamicCast(matches.front());
        if(p)
        {
            if(_unit->ignRedefs())
            {
                p->updateIncludeLevel();
                return p;
            }
        }
        if(matches.front()->name() != name)
        {
            string msg = "parameter `" + name + "' differs only in capitalization from ";
            msg += "parameter `" + matches.front()->name() + "'";
            _unit->error(msg);
        }
        else
        {
            string msg = "redefinition of parameter `" + name + "'";
            _unit->error(msg);
            return 0;
        }
    }

    string newName = IceUtilInternal::toLower(name);
    string thisName = IceUtilInternal::toLower(this->name());

    //
    // Check that in parameters don't follow out parameters.
    //
    if(!_contents.empty())
    {
        ParamDeclPtr p = ParamDeclPtr::dynamicCast(_contents.back());
        assert(p);
        if(p->isOutParam() && !isOutParam)
        {
            _unit->error("`" + name + "': in parameters cannot follow out parameters");
        }
    }

    //
    // Non-local class/interface cannot have operation with local parameters.
    //
    ClassDefPtr cl = ClassDefPtr::dynamicCast(this->container());
    assert(cl);
    if(type->isLocal() && !cl->isLocal())
    {
        string msg = "non-local " + cl->kindOf() + " `" + cl->name() + "' cannot have local parameter `";
        msg += name + "' in operation `" + this->name() + "'";
        _unit->error(msg);
    }

    //
    // Issue a warning for a deprecated parameter type.
    //
    checkDeprecatedType(_unit, type);

    if(optional)
    {
        //
        // Check for a duplicate tag.
        //
        const string msg = "tag for optional parameter `" + name + "' is already in use";
        if(_returnIsOptional && tag == _returnTag)
        {
            _unit->error(msg);
        }
        else
        {
            ParamDeclList params = parameters();
            for(ParamDeclList::const_iterator p = params.begin(); p != params.end(); ++p)
            {
                if((*p)->optional() && (*p)->tag() == tag)
                {
                    _unit->error(msg);
                    break;
                }
            }
        }
    }

    ParamDeclPtr p = new ParamDecl(this, name, type, isOutParam, optional, tag);
    _contents.push_back(p);
    return p;
}

ParamDeclList
Slice::Operation::parameters() const
{
    ParamDeclList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ParamDeclPtr q = ParamDeclPtr::dynamicCast(*p);
        if(q)
        {
            result.push_back(q);
        }
    }
    return result;
}

ParamDeclList
Slice::Operation::inParameters() const
{
    ParamDeclList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ParamDeclPtr q = ParamDeclPtr::dynamicCast(*p);
        if(q && !q->isOutParam())
        {
            result.push_back(q);
        }
    }
    return result;
}

void
Slice::Operation::inParameters(ParamDeclList& required, ParamDeclList& optional) const
{
    const ParamDeclList params = inParameters();
    for(ParamDeclList::const_iterator pli = params.begin(); pli != params.end(); ++pli)
    {
        if((*pli)->optional())
        {
            optional.push_back(*pli);
        }
        else
        {
            required.push_back(*pli);
        }
    }

    sortOptionalParameters(optional);
}

ParamDeclList
Slice::Operation::outParameters() const
{
    ParamDeclList result;
    for(ContainedList::const_iterator p = _contents.begin(); p != _contents.end(); ++p)
    {
        ParamDeclPtr q = ParamDeclPtr::dynamicCast(*p);
        if(q && q->isOutParam())
        {
            result.push_back(q);
        }
    }
    return result;
}

void
Slice::Operation::outParameters(ParamDeclList& required, ParamDeclList& optional) const
{
    const ParamDeclList params = outParameters();
    for(ParamDeclList::const_iterator pli = params.begin(); pli != params.end(); ++pli)
    {
        if((*pli)->optional())
        {
            optional.push_back(*pli);
        }
        else
        {
            required.push_back(*pli);
        }
    }

    sortOptionalParameters(optional);
}

ExceptionList
Slice::Operation::throws() const
{
    return _throws;
}

void
Slice::Operation::setExceptionList(const ExceptionList& el)
{
    _throws = el;

    //
    // Check that no exception occurs more than once in the throws clause.
    //
    ExceptionList uniqueExceptions = el;
    uniqueExceptions.sort();
    uniqueExceptions.unique();
    if(uniqueExceptions.size() != el.size())
    {
        //
        // At least one exception appears twice.
        //
        ExceptionList tmp = el;
        tmp.sort();
        ExceptionList duplicates;
        set_difference(tmp.begin(), tmp.end(),
                       uniqueExceptions.begin(), uniqueExceptions.end(),
                       back_inserter(duplicates));
        string msg = "operation `" + name() + "' has a throws clause with ";
        if(duplicates.size() == 1)
        {
            msg += "a ";
        }
        msg += "duplicate exception";
        if(duplicates.size() > 1)
        {
            msg += "s";
        }
        ExceptionList::const_iterator i = duplicates.begin();
        msg += ": `" + (*i)->name() + "'";
        for(i = ++i; i != duplicates.end(); ++i)
        {
            msg += ", `" + (*i)->name() + "'";
        }
        _unit->error(msg);
    }

    //
    // If the interface is non-local, no local exception can be thrown.
    //
    ClassDefPtr cl = ClassDefPtr::dynamicCast(container());
    assert(cl);
    if(!cl->isLocal())
    {
        for(ExceptionList::const_iterator ep = el.begin(); ep != el.end(); ++ep)
        {
            if((*ep)->isLocal())
            {
                string msg = "non-local " + cl->kindOf() + " `" + cl->name() + "' cannot have operation `";
                msg += name() + "' throwing local exception `" + (*ep)->name() + "'";
                _unit->error(msg);
            }
        }
    }
}

Contained::ContainedType
Slice::Operation::containedType() const
{
    return ContainedTypeOperation;
}

bool
Slice::Operation::uses(const ContainedPtr& contained) const
{
    {
        ContainedPtr contained2 = ContainedPtr::dynamicCast(_returnType);
        if(contained2 && contained2 == contained)
        {
            return true;
        }
    }

    for(ExceptionList::const_iterator q = _throws.begin(); q != _throws.end(); ++q)
    {
        ContainedPtr contained2 = ContainedPtr::dynamicCast(*q);
        if(contained2 && contained2 == contained)
        {
            return true;
        }
    }

    return false;
}

bool
Slice::Operation::sendsClasses(bool includeOptional) const
{
    ParamDeclList pdl = parameters();
    for(ParamDeclList::const_iterator i = pdl.begin(); i != pdl.end(); ++i)
    {
        if(!(*i)->isOutParam() && (*i)->type()->usesClasses() && (includeOptional || !(*i)->optional()))
        {
            return true;
        }
    }
    return false;
}

bool
Slice::Operation::returnsClasses(bool includeOptional) const
{
    TypePtr t = returnType();
    if(t && t->usesClasses() && (includeOptional || !_returnIsOptional))
    {
        return true;
    }
    ParamDeclList pdl = parameters();
    for(ParamDeclList::const_iterator i = pdl.begin(); i != pdl.end(); ++i)
    {
        if((*i)->isOutParam() && (*i)->type()->usesClasses() && (includeOptional || !(*i)->optional()))
        {
            return true;
        }
    }
    return false;
}

bool
Slice::Operation::returnsData() const
{
    TypePtr t = returnType();
    if(t)
    {
        return true;
    }
    ParamDeclList pdl = parameters();
    for(ParamDeclList::const_iterator i = pdl.begin(); i != pdl.end(); ++i)
    {
        if((*i)->isOutParam())
        {
            return true;
        }
    }
    if(!throws().empty())
    {
        return true;
    }
    return false;
}

bool
Slice::Operation::returnsMultipleValues() const
{
    size_t count = outParameters().size();

    if(returnType())
    {
        ++count;
    }

    return count > 1;
}

bool
Slice::Operation::sendsOptionals() const
{
    ParamDeclList pdl = inParameters();
    for(ParamDeclList::const_iterator i = pdl.begin(); i != pdl.end(); ++i)
    {
        if((*i)->optional())
        {
            return true;
        }
    }
    return false;
}

int
Slice::Operation::attributes() const
{
    string freezeMD;

    if(!findMetaData("freeze:", freezeMD))
    {
        ClassDefPtr classDef = ClassDefPtr::dynamicCast(container());
        assert(classDef != 0);
        classDef->findMetaData("freeze:", freezeMD);
    }

    if(freezeMD != "")
    {
        int result = 0;

        freezeMD = freezeMD.substr(strlen("freeze:"));

        int i = 0;
        while(i < 2)
        {
            if(freezeMD.find(readWriteAttribute[i]) == 0)
            {
                result = i;
                freezeMD = freezeMD.substr(readWriteAttribute[i].size());
                break; // while
            }
            i++;
        }
        if(i == 2)
        {
            _unit->warning(InvalidMetaData, "invalid freeze metadata for operation");
        }
        else
        {
            if(freezeMD.size() == 0)
            {
                freezeMD = (result == 0) ? ":supports" : ":required";
            }

            //
            // Remove ":"
            //
            freezeMD = freezeMD.substr(1);

            i = 0;
            while(i < 4)
            {
                if(freezeMD.find(txAttribute[i]) == 0)
                {
                    if(result != 0 && (i == int(Supports) || i == int(Never)))
                    {
                        _unit->warning(InvalidMetaData, "invalid freeze metadata for operation");
                    }
                    else
                    {
                        result |= (i << 1);
                    }
                    freezeMD = freezeMD.substr(txAttribute[i].size());
                    break; // while
                }
                i++;
            }

            if(i == 4)
            {
                _unit->warning(InvalidMetaData, "invalid freeze metadata for operation");

                //
                // Set default
                //
                if(result != 0)
                {
                    result |= (int(Required) << 1);
                }
            }
        }
        return result;
    }
    else
    {
        return 0;
    }
}

FormatType
Slice::Operation::format() const
{
    FormatType format = parseFormatMetaData(getMetaData());
    if(format == DefaultFormat)
    {
        ContainedPtr cont = ContainedPtr::dynamicCast(container());
        assert(cont);
        format = parseFormatMetaData(cont->getMetaData());
    }
    return format;
}

string
Slice::Operation::kindOf() const
{
    return "operation";
}

void
Slice::Operation::visit(ParserVisitor* visitor, bool)
{
    visitor->visitOperation(this);
}

Slice::Operation::Operation(const ContainerPtr& container,
                            const string& name,
                            const TypePtr& returnType,
                            bool returnIsOptional,
                            int returnTag,
                            Mode mode) :
    SyntaxTreeBase(container->unit()),
    Contained(container, name),
    Container(container->unit()),
    _returnType(returnType),
    _returnIsOptional(returnIsOptional),
    _returnTag(returnTag),
    _mode(mode)
{
    if(returnType)
    {
        checkDeprecatedType(_unit, returnType);
    }
}

// ----------------------------------------------------------------------
// ParamDecl
// ----------------------------------------------------------------------

TypePtr
Slice::ParamDecl::type() const
{
    return _type;
}

bool
Slice::ParamDecl::isOutParam() const
{
    return _isOutParam;
}

bool
Slice::ParamDecl::optional() const
{
    return _optional;
}

int
Slice::ParamDecl::tag() const
{
    return _tag;
}

Contained::ContainedType
Slice::ParamDecl::containedType() const
{
    return ContainedTypeDataMember;
}

bool
Slice::ParamDecl::uses(const ContainedPtr& contained) const
{
    ContainedPtr contained2 = ContainedPtr::dynamicCast(_type);
    if(contained2 && contained2 == contained)
    {
        return true;
    }

    return false;
}

string
Slice::ParamDecl::kindOf() const
{
    return "parameter declaration";
}

void
Slice::ParamDecl::visit(ParserVisitor* visitor, bool)
{
    visitor->visitParamDecl(this);
}

Slice::ParamDecl::ParamDecl(const ContainerPtr& container, const string& name, const TypePtr& type, bool isOutParam,
                            bool optional, int tag) :
    SyntaxTreeBase(container->unit()),
    Contained(container, name),
    _type(type),
    _isOutParam(isOutParam),
    _optional(optional),
    _tag(tag)
{
}

// ----------------------------------------------------------------------
// DataMember
// ----------------------------------------------------------------------

TypePtr
Slice::DataMember::type() const
{
    return _type;
}

bool
Slice::DataMember::optional() const
{
    return _optional;
}

int
Slice::DataMember::tag() const
{
    return _tag;
}

string
Slice::DataMember::defaultValue() const
{
    return _defaultValue;
}

string
Slice::DataMember::defaultLiteral() const
{
    return _defaultLiteral;
}

SyntaxTreeBasePtr
Slice::DataMember::defaultValueType() const
{
    return _defaultValueType;
}

Contained::ContainedType
Slice::DataMember::containedType() const
{
    return ContainedTypeDataMember;
}

bool
Slice::DataMember::uses(const ContainedPtr& contained) const
{
    ContainedPtr contained2 = ContainedPtr::dynamicCast(_type);
    if(contained2 && contained2 == contained)
    {
        return true;
    }

    return false;
}

string
Slice::DataMember::kindOf() const
{
    return "data member";
}

void
Slice::DataMember::visit(ParserVisitor* visitor, bool)
{
    visitor->visitDataMember(this);
}

Slice::DataMember::DataMember(const ContainerPtr& container, const string& name, const TypePtr& type,
                              bool optional, int tag, const SyntaxTreeBasePtr& defaultValueType,
                              const string& defaultValue, const string& defaultLiteral) :
    SyntaxTreeBase(container->unit()),
    Contained(container, name),
    _type(type),
    _optional(optional),
    _tag(tag),
    _defaultValueType(defaultValueType),
    _defaultValue(defaultValue),
    _defaultLiteral(defaultLiteral)
{
}

// ----------------------------------------------------------------------
// Unit
// ----------------------------------------------------------------------

UnitPtr
Slice::Unit::createUnit(bool ignRedefs, bool all, bool allowIcePrefix, bool allowUnderscore,
                        const StringList& defaultGlobalMetadata)
{
    return new Unit(ignRedefs, all, allowIcePrefix, allowUnderscore, defaultGlobalMetadata);
}

bool
Slice::Unit::ignRedefs() const
{
    return _ignRedefs;
}

bool
Slice::Unit::allowIcePrefix() const
{
    return _allowIcePrefix;
}

bool
Slice::Unit::allowUnderscore() const
{
    return _allowUnderscore;
}

void
Slice::Unit::setComment(const string& comment)
{
    _currentComment = "";

    string::size_type end = 0;
    while(true)
    {
        string::size_type begin;
        if(end == 0)
        {
            //
            // Skip past the initial whitespace.
            //
            begin = comment.find_first_not_of(" \t\r\n*", end);
        }
        else
        {
            //
            // Skip more whitespace but retain blank lines.
            //
            begin = comment.find_first_not_of(" \t*", end);
        }

        if(begin == string::npos)
        {
            break;
        }

        end = comment.find('\n', begin);
        if(end != string::npos)
        {
            if(end + 1 > begin)
            {
                _currentComment += comment.substr(begin, end + 1 - begin);
            }
            ++end;
        }
        else
        {
            end = comment.find_last_not_of(" \t\r\n*");
            if(end != string::npos)
            {
                if(end + 1 > begin)
                {
                    _currentComment += comment.substr(begin, end + 1 - begin);
                }
            }
            break;
        }
    }
}

string
Slice::Unit::currentComment()
{
    string comment = "";
    comment.swap(_currentComment);
    return comment;
}

string
Slice::Unit::currentFile() const
{
    DefinitionContextPtr dc = currentDefinitionContext();
    if(dc)
    {
        return dc->filename();
    }
    else
    {
        return string();
    }
}

string
Slice::Unit::topLevelFile() const
{
    return _topLevelFile;
}

int
Slice::Unit::currentLine() const
{
    return _currentLine;
}

void
Slice::Unit::nextLine()
{
    _currentLine++;
}

bool
Slice::Unit::scanPosition(const char* s)
{
    assert(*s == '#');

    string line(s + 1);                      // Skip leading #
    eraseWhiteSpace(line);
    if(line.find("line", 0) == 0)            // Erase optional "line"
    {
        line.erase(0, 4);
        eraseWhiteSpace(line);
    }

    string::size_type idx;

    _currentLine = atoi(line.c_str()) - 1;   // Read line number

    idx = line.find_first_of(" \t\r");       // Erase line number
    if(idx != string::npos)
    {
        line.erase(0, idx);
    }
    eraseWhiteSpace(line);

    string currentFile;
    if(!line.empty())
    {
        if(line[0] == '"')
        {
            idx = line.rfind('"');
            if(idx != string::npos)
            {
                currentFile = line.substr(1, idx - 1);
            }
        }
        else
        {
            currentFile = line;
        }
    }

    enum LineType { File, Push, Pop };

    LineType type = File;

    if(_currentLine == 0)
    {
        if(_currentIncludeLevel > 0 || currentFile != _topLevelFile)
        {
            type = Push;
            line.erase(idx);
            eraseWhiteSpace(line);
        }
    }
    else
    {
        DefinitionContextPtr dc = currentDefinitionContext();
        if(dc != 0 && !dc->filename().empty() && dc->filename() != currentFile)
        {
            type = Pop;
            line.erase(idx);
            eraseWhiteSpace(line);
        }
    }

    switch(type)
    {
        case Push:
        {
            if(++_currentIncludeLevel == 1)
            {
                if(find(_includeFiles.begin(), _includeFiles.end(), currentFile) == _includeFiles.end())
                {
                    _includeFiles.push_back(currentFile);
                }
            }
            pushDefinitionContext();
            _currentComment = "";
            break;
        }
        case Pop:
        {
            --_currentIncludeLevel;
            popDefinitionContext();
            _currentComment = "";
            break;
        }
        default:
        {
            break; // Do nothing
        }
    }
    if(!currentFile.empty())
    {
        DefinitionContextPtr dc = currentDefinitionContext();
        assert(dc);
        dc->setFilename(currentFile);
        _definitionContextMap.insert(make_pair(currentFile, dc));
    }

    //
    // Return code indicates whether starting parse of a new file.
    //
    return _currentLine == 0;
}

int
Slice::Unit::currentIncludeLevel() const
{
    if(_all)
    {
        return 0;
    }
    else
    {
        return _currentIncludeLevel;
    }
}

void
Slice::Unit::addGlobalMetaData(const StringList& metaData)
{
    DefinitionContextPtr dc = currentDefinitionContext();
    assert(dc);
    if(dc->seenDefinition())
    {
        error("file metadata must appear before any definitions");
    }
    else
    {
        //
        // Append the file metadata to any existing metadata (e.g., default file metadata).
        //
        StringList l = dc->getMetaData();
        copy(metaData.begin(), metaData.end(), back_inserter(l));
        dc->setMetaData(l);
    }
}

void
Slice::Unit::setSeenDefinition()
{
    DefinitionContextPtr dc = currentDefinitionContext();
    assert(dc);
    dc->setSeenDefinition();
}

void
Slice::Unit::error(const string& s)
{
    emitError(currentFile(), _currentLine, s);
    _errors++;
}

void
Slice::Unit::warning(WarningCategory category, const string& msg) const
{
    if(_definitionContextStack.empty())
    {
        emitWarning(currentFile(), _currentLine, msg);
    }
    else
    {
        _definitionContextStack.top()->warning(category, currentFile(), _currentLine, msg);
    }
}

ContainerPtr
Slice::Unit::currentContainer() const
{
    assert(!_containerStack.empty());
    return _containerStack.top();
}

void
Slice::Unit::pushContainer(const ContainerPtr& cont)
{
    _containerStack.push(cont);
}

void
Slice::Unit::popContainer()
{
    assert(!_containerStack.empty());
    _containerStack.pop();
}

DefinitionContextPtr
Slice::Unit::currentDefinitionContext() const
{
    DefinitionContextPtr dc;
    if(!_definitionContextStack.empty())
    {
        dc = _definitionContextStack.top();
    }
    return dc;
}

void
Slice::Unit::pushDefinitionContext()
{
    _definitionContextStack.push(new DefinitionContext(_currentIncludeLevel, _defaultGlobalMetaData));
}

void
Slice::Unit::popDefinitionContext()
{
    assert(!_definitionContextStack.empty());
    _definitionContextStack.pop();
}

DefinitionContextPtr
Slice::Unit::findDefinitionContext(const string& file) const
{
    map<string, DefinitionContextPtr>::const_iterator p = _definitionContextMap.find(file);
    if(p != _definitionContextMap.end())
    {
        return p->second;
    }
    return 0;
}

void
Slice::Unit::addContent(const ContainedPtr& contained)
{
    string scoped = IceUtilInternal::toLower(contained->scoped());
    _contentMap[scoped].push_back(contained);
}

void
Slice::Unit::removeContent(const ContainedPtr& contained)
{
    string scoped = IceUtilInternal::toLower(contained->scoped());
    map<string, ContainedList>::iterator p = _contentMap.find(scoped);
    assert(p != _contentMap.end());
    for(ContainedList::iterator q = p->second.begin(); q != p->second.end(); ++q)
    {
        if(q->get() == contained.get())
        {
            p->second.erase(q);
            return;
        }
    }
    assert(false);
}

ContainedList
Slice::Unit::findContents(const string& scoped) const
{
    assert(!scoped.empty());
    assert(scoped[0] == ':');

    string name = IceUtilInternal::toLower(scoped);
    map<string, ContainedList>::const_iterator p = _contentMap.find(name);
    if(p != _contentMap.end())
    {
        return p->second;
    }
    else
    {
        return ContainedList();
    }
}

ClassList
Slice::Unit::findDerivedClasses(const ClassDefPtr& cl) const
{
    ClassList derived;
    for(map<string, ContainedList>::const_iterator p = _contentMap.begin(); p != _contentMap.end(); ++p)
    {
        for(ContainedList::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
        {
            ClassDefPtr r = ClassDefPtr::dynamicCast(*q);
            if(r)
            {
                ClassList bases = r->bases();
                if(find(bases.begin(), bases.end(), cl) != bases.end())
                {
                    derived.push_back(r);
                }
            }
        }
    }
    derived.sort();
    derived.unique();
    return derived;
}

ExceptionList
Slice::Unit::findDerivedExceptions(const ExceptionPtr& ex) const
{
    ExceptionList derived;
    for(map<string, ContainedList>::const_iterator p = _contentMap.begin(); p != _contentMap.end(); ++p)
    {
        for(ContainedList::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
        {
            ExceptionPtr r = ExceptionPtr::dynamicCast(*q);
            if(r)
            {
                ExceptionPtr base = r->base();
                if(base && base == ex)
                {
                    derived.push_back(r);
                }
            }
        }
    }
    derived.sort();
    derived.unique();
    return derived;
}

ContainedList
Slice::Unit::findUsedBy(const ContainedPtr& contained) const
{
    ContainedList usedBy;
    for(map<string, ContainedList>::const_iterator p = _contentMap.begin(); p != _contentMap.end(); ++p)
    {
        for(ContainedList::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
        {
            if((*q)->uses(contained))
            {
                usedBy.push_back(*q);
            }
        }
    }
    usedBy.sort();
    usedBy.unique();
    return usedBy;
}

void
Slice::Unit::addTypeId(int compactId, const std::string& typeId)
{
    _typeIds.insert(make_pair(compactId, typeId));
}

std::string
Slice::Unit::getTypeId(int compactId) const
{
    map<int, string>::const_iterator p = _typeIds.find(compactId);
    if(p != _typeIds.end())
    {
        return p->second;
    }
    return string();
}

bool
Slice::Unit::hasCompactTypeId() const
{
    return _typeIds.size() > 0;
}

bool
Slice::Unit::usesNonLocals() const
{
    for(map<string, ContainedList>::const_iterator p = _contentMap.begin(); p != _contentMap.end(); ++p)
    {
        for(ContainedList::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
        {
            ConstructedPtr constr = ConstructedPtr::dynamicCast(*q);
            if(constr && !constr->isLocal())
            {
                return true;
            }

            ExceptionPtr exc = ExceptionPtr::dynamicCast(*q);
            if(exc && !exc->isLocal())
            {
                return true;
            }
        }
    }

    if(_builtins.find(Builtin::KindObject) != _builtins.end())
    {
        return true;
    }

    if(_builtins.find(Builtin::KindObjectProxy) != _builtins.end())
    {
        return true;
    }

    if(_builtins.find(Builtin::KindValue) != _builtins.end())
    {
        return true;
    }

    return false;
}

bool
Slice::Unit::usesConsts() const
{
    for(map<string, ContainedList>::const_iterator p = _contentMap.begin(); p != _contentMap.end(); ++p)
    {
        for(ContainedList::const_iterator q = p->second.begin(); q != p->second.end(); ++q)
        {
            ConstPtr cd = ConstPtr::dynamicCast(*q);
            if(cd)
            {
                return true;
            }
        }
    }

    return false;
}

StringList
Slice::Unit::includeFiles() const
{
    return _includeFiles;
}

StringList
Slice::Unit::allFiles() const
{
    StringList result;
    for(map<string, DefinitionContextPtr>::const_iterator p = _definitionContextMap.begin();
        p != _definitionContextMap.end(); ++p)
    {
        result.push_back(p->first);
    }
    return result;
}

int
Slice::Unit::parse(const string& filename, FILE* file, bool debug)
{
    slice_debug = debug ? 1 : 0;

    assert(!Slice::unit);
    Slice::unit = this;

    _currentComment = "";
    _currentLine = 1;
    _currentIncludeLevel = 0;
    _topLevelFile = fullPath(filename);
    pushContainer(this);
    pushDefinitionContext();

    //
    // MCPP Fix: mcpp doesn't always output the first #line when mcpp_lib_main is
    // called repeatedly. We scan a fake #line here to ensure the top definition
    // context is correctly initialized.
    //
    scanPosition(string("#line 1 " + _topLevelFile).c_str());

    slice_in = file;
    int status = slice_parse();
    if(_errors)
    {
        status = EXIT_FAILURE;
    }

    if(status == EXIT_FAILURE)
    {
        while(!_containerStack.empty())
        {
            popContainer();
        }
        while(!_definitionContextStack.empty())
        {
            popDefinitionContext();
        }
    }
    else
    {
        assert(_containerStack.size() == 1);
        popContainer();
        assert(_definitionContextStack.size() == 1);
        popDefinitionContext();
    }

    Slice::unit = 0;
    return status;
}

void
Slice::Unit::destroy()
{
    _contentMap.clear();
    _builtins.clear();
    Container::destroy();
}

void
Slice::Unit::visit(ParserVisitor* visitor, bool all)
{
    if(visitor->visitUnitStart(this))
    {
        Container::visit(visitor, all);
        visitor->visitUnitEnd(this);
    }
}

BuiltinPtr
Slice::Unit::builtin(Builtin::Kind kind)
{
    map<Builtin::Kind, BuiltinPtr>::const_iterator p = _builtins.find(kind);
    if(p != _builtins.end())
    {
        return p->second;
    }
    BuiltinPtr builtin = new Builtin(this, kind);
    _builtins.insert(make_pair(kind, builtin));
    return builtin;
}

void
Slice::Unit::addTopLevelModule(const string& file, const string& module)
{
    map<string, set<string> >::iterator i = _fileTopLevelModules.find(file);
    if(i == _fileTopLevelModules.end())
    {
        set<string> modules;
        modules.insert(module);
        _fileTopLevelModules.insert(make_pair(file, modules));
    }
    else
    {
        i->second.insert(module);
    }
}
set<string>
Slice::Unit::getTopLevelModules(const string& file) const
{
    map<string, set<string> >::const_iterator i = _fileTopLevelModules.find(file);
    if(i == _fileTopLevelModules.end())
    {
        return set<string>();
    }
    else
    {
        return i->second;
    }
}

Slice::Unit::Unit(bool ignRedefs, bool all, bool allowIcePrefix, bool allowUnderscore,
                  const StringList& defaultGlobalMetadata) :
    SyntaxTreeBase(0),
    Container(0),
    _ignRedefs(ignRedefs),
    _all(all),
    _allowIcePrefix(allowIcePrefix),
    _allowUnderscore(allowUnderscore),
    _defaultGlobalMetaData(defaultGlobalMetadata),
    _errors(0),
    _currentLine(0),
    _currentIncludeLevel(0)

{
    _unit = this;
}

void
Slice::Unit::eraseWhiteSpace(string& s)
{
    string::size_type idx = s.find_first_not_of(" \t\r");
    if(idx != string::npos)
    {
        s.erase(0, idx);
    }
    idx = s.find_last_not_of(" \t\r");
    if(idx != string::npos)
    {
        s.erase(++idx);
    }
}

// ----------------------------------------------------------------------
// CICompare
// ----------------------------------------------------------------------

bool
Slice::CICompare::operator()(const string& s1, const string& s2) const
{
    string::const_iterator p1 = s1.begin();
    string::const_iterator p2 = s2.begin();
    while(p1 != s1.end() && p2 != s2.end() &&
          ::tolower(static_cast<unsigned char>(*p1)) == ::tolower(static_cast<unsigned char>(*p2)))
    {
        ++p1;
        ++p2;
    }
    if(p1 == s1.end() && p2 == s2.end())
    {
        return false;
    }
    else if(p1 == s1.end())
    {
        return true;
    }
    else if(p2 == s2.end())
    {
        return false;
    }
    else
    {
        return ::tolower(static_cast<unsigned char>(*p1)) < ::tolower(static_cast<unsigned char>(*p2));
    }
}

#if defined(__SUNPRO_CC)
bool
Slice::cICompare(const std::string& s1, const std::string& s2)
{
    CICompare c;
    return c(s1, s2);
}
#endif

// ----------------------------------------------------------------------
// DerivedToBaseCompare
// ----------------------------------------------------------------------

bool
Slice::DerivedToBaseCompare::operator()(const ExceptionPtr& e1, const ExceptionPtr& e2) const
{
    return e2->isBaseOf(e1);
}

#if defined(__SUNPRO_CC)
bool
Slice::derivedToBaseCompare(const ExceptionPtr& e1, const ExceptionPtr& e2)
{
    return e2->isBaseOf(e1);
}
#endif
