/* vim: set ts=4 sw=4 tw=99 et:
 *
 * Copyright (C) 2012 David Anderson
 *
 * This file is part of JITCraft.
 *
 * JITCraft is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 * 
 * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * JITCraft. If not, see http://www.gnu.org/licenses/.
 */
#include <string.h>
#include "SemanticAnalysis.h"
#include "Types.h"
#include "Zone.h"
#include "TypeManager.h"
#include "Scopes.h"
#include "NameBinding.h"
#include "Strings.h"
#include "Box.h"
#include "Array.h"
#include "Heap-inl.h"

using namespace ke;

static inline bool
IsPrimitiveType(Type *type, PrimitiveType primitive)
{
    if (!type)
        return false;

    if (!type->isPrimitive())
        return false;

    return primitive == type->primitive();
}

static inline bool
EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right,
                        BoxedPrimitive *out)
{
    switch (kind) {
      case TOK_PLUS:
        *out = BoxedPrimitive::Int(left.toInt() + right.toInt());
        return true;

      case TOK_SHL:
        *out = BoxedPrimitive::Int(left.toInt() << right.toInt());
        return true;

      default:
        return false;
    }
}

static inline bool
EvaluateUnaryOperation(TokenKind kind, const BoxedPrimitive &inner, BoxedPrimitive *out)
{
  switch (kind) {
    case TOK_MINUS:
      *out = BoxedPrimitive::Int(-inner.toInt());
      return true;

    default:
      return false;
  }
}

static bool
EvaluateForConstant(Expression *expression, BoxedPrimitive *out)
{
    switch (expression->kind()) {
      case AstNode::kBinaryExpression:
      {
        BoxedPrimitive left;
        BoxedPrimitive right;
        BinaryExpression *b = expression->toBinaryExpression();
        if (!EvaluateForConstant(b->left(), &left) ||
            !EvaluateForConstant(b->right(), &right))
        {
            return false;
        }
        return EvaluateBinaryOperation(b->token(), left, right, out);
      }

      case AstNode::kUnaryExpression:
      {
        BoxedPrimitive inner;
        UnaryExpression *u = expression->toUnaryExpression();
        if (!EvaluateForConstant(u->expression(), &inner))
            return false;
        return EvaluateUnaryOperation(u->token(), inner, out);
      }

      case AstNode::kNameProxy:
      {
        NameProxy *proxy = expression->toNameProxy();
        if (proxy->binding()->kind() != BoundName::CONSTANT)
            return false;

        NamedConstant *c = proxy->binding()->toNamedConstant();
        if (!IsPrimitiveType(c->type(), PrimitiveType_Int32))
            return false;

        *out = c->value();
        return true;
      }

      case AstNode::kBooleanLiteral:
        *out = BoxedPrimitive::Int(expression->toBooleanLiteral()->token() == TOK_TRUE ? 1 : 0);
        return true;

      case AstNode::kIntegerLiteral:
        *out = BoxedPrimitive::Int(expression->toIntegerLiteral()->value());
        return true;

      default:
        return false;
    }
}

SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree)
  : zone_(zone),
    cc_(cc),
    tree_(tree),
    scope_(NULL),
    loop_(NULL)
{
}

bool
SemanticAnalysis::analyze()
{
    if (cc_.failures())
        return false;

    scope_ = tree_->scope();

    for (size_t i = 0; i < tree_->statements()->length(); i++) {
        Statement *statement = tree_->statements()->at(i);
        if (statement->isFunctionStatement()) {
            FunctionStatement *funstmt = statement->toFunctionStatement();
            if (!preanalyze(funstmt))
                return false;
        } else if (statement->isEnumStatement()) {
            EnumStatement *enumstmt = statement->toEnumStatement();

            // We pre-fill global enum types, since they may occur in function
            // signatures. This should probably be somewhere else...
            enumstmt->accept(this);
        } else if (statement->isStructureStatement()) {
            StructureStatement *node = statement->toStructureStatement();
            node->accept(this);
            if (!node->decl()->type())
                return false;
        } else if (statement->isFunctionTypeStatement()) {
            FunctionTypeStatement *node = statement->toFunctionTypeStatement();
            node->accept(this);
            if (!node->decl()->type())
                return false;
        }
    }

    for (size_t i = 0; i < tree_->statements()->length(); i++) {
        Statement *statement = tree_->statements()->at(i);

        // We can't double-process these. Maybe put this check somewhere else.
        if (statement->isEnumStatement())
            continue;

        statement->accept(this);
    }

    if (!determineLiveFunctions())
        return false;

    return !cc_.failures();
}

bool
SemanticAnalysis::determineLiveFunctions()
{
    Vector<NamedFunction *> worklist;

    // Add the root set.
    for (size_t i = 0; i < tree_->statements()->length(); i++) {
        Statement *statement = tree_->statements()->at(i);
        if (!statement->isFunctionStatement())
            continue;

        FunctionStatement *stmt = statement->toFunctionStatement();
        NamedFunction  *fun = stmt->fun();
        if (!fun->used())
            continue;

        for (size_t i = 0; i < fun->usedGlobals()->length(); i++) {
            NamedFunction *name = fun->usedGlobals()->at(i)->toNamedFunction();

            // Names in these lists are always unused, however, we mark them
            // as used while inserting into the work queue, to avoid double-
            // insertion.
            if (name->used())
                continue;

            if (!worklist.append(name))
                return false;

            name->setUsed();
        }
    }

    while (!worklist.empty()) {
        NamedFunction *fun = worklist.popCopy();

        for (size_t i = 0; i < fun->usedGlobals()->length(); i++) {
            NamedFunction *name = fun->usedGlobals()->at(i)->toNamedFunction();
            if (name->used())
                continue;
            if (!worklist.append(name))
                return false;
            name->setUsed();
        }
    }

    return true;
}

Type *
SemanticAnalysis::bindType(const SourcePosition &pos, BoundName *name)
{
    if (!name)
        return zone_->types()->getPrimitive(PrimitiveType_Int32);

    if (name->kind() == BoundName::UNBOUND) {
        cc_.reportError(pos, Message_TypeNotFound, name->name()->chars());
        return NULL;
    }

    if (name->kind() != BoundName::TYPE) {
        cc_.reportError(pos, Message_IdentifierIsNotAType, name->name()->chars());
        return NULL;
    }

    if (!name->type())
        return NULL;

    return name->type();
}

Type *
SemanticAnalysis::visitAndApplyType(Expression *expression)
{
    expression->accept(this);
    return expression->type();
}

Type *
SemanticAnalysis::visitForLoad(Expression *expression)
{
    Local<Type> type(zone_, visitAndApplyType(expression));
    if (!type)
        return NULL;

    if (type->isReference()) {
        expression->setType(type->contained());
    } else if (expression->isCallExpression()) {
        if (type->isVoid()) {
            CallExpression *call = expression->toCallExpression();
            cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue);
            return NULL;
        }
    }

    return expression->type();
}

// :TODO: this leaks an interior pointer.
const char *
ke::GetTypeName(Type *type)
{
    if (type->isArray())
        return "array";
    if (type->isFunction())
        return "function";
    if (type->isVoid())
        return "void";
    if (type->isEnum() || type->isStruct())
        return type->name()->chars();
    if (type->isReference())
        type = type->contained();
    switch (type->primitive()) {
      case PrimitiveType_Bool:
        return "bool";
      case PrimitiveType_Char:
        return "String";
      case PrimitiveType_Int32:
        return "int";
      case PrimitiveType_Float:
        return "Float";
      default:
        return "<unknown>";
    }
}

void
SemanticAnalysis::unaryTypeError(AstNode *node, TokenKind token, Handle<Type> type)
{
    const char *op = TokenNames[token];
    const char *name = GetTypeName(type);

    cc_.reportError(node->pos(), Message_UnaryTypeError, op, name);
}

void
SemanticAnalysis::binaryTypeError(AstNode *node, TokenKind token, Handle<Type> left, Handle<Type> right)
{
    const char *op = TokenNames[token];
    const char *leftName = GetTypeName(left);
    const char *rightName = GetTypeName(right);

    cc_.reportError(node->pos(), Message_BinaryTypeError, op, leftName, rightName);
}

Type *
SemanticAnalysis::typeCheckBinary(AstNode *node, TokenKind token, Handle<Type> left, Handle<Type> right)
{
    if ((token >= TOK_PLUS && token <= TOK_SLASH) ||
        (token >= TOK_ASSIGN_ADD && token <= TOK_ASSIGN_DIV))
    {
        // If either side is a float, the other side must be a float or int.
        if (left->isFloat() || right->isFloat()) {
            Type *floatType = left->isFloat() ? left : right;
            Type *other = left->isFloat() ? right : left;
            if (other->isFloat() || other->isInt32())
                return floatType;
        }
    }

    if (token == TOK_OR || token == TOK_AND) {
        if (!canTest(node, left) || !canTest(node, right))
            return NULL;
        return zone_->types()->getPrimitive(PrimitiveType_Bool);
    }

    if (token >= TOK_EQUALS && token <= TOK_GE) {
        if ((left->isFloat() || left->isInt32()) &&
            (right->isFloat() || right->isInt32()))
        {
            return zone_->types()->getPrimitive(PrimitiveType_Bool);
        }
        binaryTypeError(node, token, left, right);
        return NULL;
    }

    if (!left->isInt32() || !right->isInt32()) {
        binaryTypeError(node, token, left, right);
        return NULL;
    }

    return left;
}

bool
SemanticAnalysis::canTest(AstNode *node, Handle<Type> type)
{
    if (!type->isPrimitive() && !type->isEnum()) {
        cc_.reportError(node->pos(), Message_ArrayIsNotBoolean);
        return false;
    }
    return true;
}

Type *
SemanticAnalysis::visitForLValue(Expression *expression)
{
    if (expression->isIndexExpression()) {
        Type *type = visitAndApplyType(expression);
        if (!type)
            return NULL;

        // if (type->isConst()) {
        //     cc_.reportError(expression->pos(), Message_ReferenceIsConstant);
        //     return NULL;
        // }

        return type;
    }

    if (expression->isFieldExpression()) {
        Type *type = visitAndApplyType(expression);
        if (!type)
            return NULL;

        // if (expression->toFieldExpression()->left()->type()->isConst()) {
        //     cc_.reportError(expression->pos(), Message_ReferenceIsConstant);
        //     return NULL;
        // }

        return type;
    }

    if (!expression->isNameProxy()) {
        cc_.reportError(expression->pos(), Message_ExpectedLValue);
        return NULL;
    }

    NameProxy *proxy = expression->toNameProxy();
    if (!proxy->binding()->kind() == BoundName::VARIABLE) {
        cc_.reportError(expression->pos(), Message_ExpectedLValue);
        return NULL;
    }

    Variable *var = proxy->binding()->toVariable();
    if (!var->type())
        return NULL;

    if  (var->type()->isReference()) {
        if (var->type()->isConst()) {
            cc_.reportError(expression->pos(), Message_ReferenceIsConstant);
            return NULL;
        }
        return var->type()->contained();
    }
    return var->type();
}

SemanticAnalysis::ControlStatus
SemanticAnalysis::visitForControl(Statement *statement)
{
    ControlScope scope(&control_);
    statement->accept(this);
    return scope.status();
}

void
SemanticAnalysis::visit(NameProxy *name)
{
    BoundName *binding = name->binding();

    if (binding->scope()->kind() == Scope::GLOBAL) {
        if (!function_ || function_->isPublic()) {
            // Whatever we're referencing must be kept alive no matter what.
            // As such, we simply mark it used, and don't do anything special.
            binding->setUsed();
        } else if (!binding->used() && binding->kind() == BoundName::FUNCTION) {
            // Otherwise, we're inside a normal stock or function. Since we
            // don't really know if we've been used yet, we shouldn't
            // prematurely references as alive. Instead, we keep a list of
            // which names have been used, so we can mark them later if needed.
            //
            // Note that we skip this step if the binding is already in the
            // root set.
            if (!function_->addUsedGlobal(binding))
                return;
        }
    }

    name->setType(binding->type());
}

void
SemanticAnalysis::visit(Assignment *node)
{
    Local<Type> exprType(zone_, visitForLoad(node->expression()));
    if (!exprType)
        return ;

    Local<Type> type(zone_, visitForLValue(node->lvalue()));
    if (!type)
        return;

    if (node->token() != TOK_ASSIGN) {
        if (!typeCheckBinary(node, node->token(), type, exprType))
            return;
    }

    if (!typeCheckAssignment(node->pos(), exprType, type, true))
        return;

    node->setType(type);
}

void
SemanticAnalysis::visit(IncDecExpression *node)
{
    Local<Type> type(zone_, visitForLValue(node->expression()));
    if (!type)
        return;

    assert(!type->isReference());

    if (!type->isInt32() && !type->isFloat()) {
        unaryTypeError(node, node->token(), type);
        return;
    }

    node->setType(type);
}

void
SemanticAnalysis::visit(UnaryExpression *node)
{
    Local<Type> type(zone_, visitForLoad(node->expression()));
    if (!type)
        return;

    switch (node->token())
    {
      case TOK_NOT:
        if (!canTest(node->expression(), type))
            return;
        type = zone_->types()->getPrimitive(PrimitiveType_Int32);
        if (!type)
            return;
        node->setType(type);
        break;

      case TOK_MINUS:
        if (!type->isFloat() && !type->isInt32()) {
            unaryTypeError(node, node->token(), type);
            return;
        }
        node->setType(type);
        break;

      case TOK_LABEL:
      {
        Local<Type> to(zone_, bindType(node->pos(), node->type()));
        if (!to)
            return;

        if ((!type->isFloat() && !type->isInt32OrEnum()) ||
            (!to->isFloat() && !to->isInt32OrEnum()))
        {
            unaryTypeError(node, node->token(), type);
            return;
        }

        node->setType(to);
        break;
      }

      default:
        assert(node->token() == TOK_TILDE);
        if (!type->isInt32()) {
            unaryTypeError(node, node->token(), type);
            return;
        }
        node->setType(type);
        break;
    }
}

void
SemanticAnalysis::visit(TernaryExpression *node)
{
    Local<Type> type(zone_, visitForLoad(node->condition()));
    if (!type)
        return;

    if (!canTest(node->condition(), type))
        return;

    Local<Type> left(zone_, visitForLoad(node->left()));
    Local<Type> right(zone_, visitForLoad(node->right()));
    if (!left || !right)
        return;

    if (left != right) {
        binaryTypeError(node, TOK_QMARK, left, right);
        return;
    }
}

void
SemanticAnalysis::visit(BinaryExpression *node)
{
    Local<Type> left(zone_, visitForLoad(node->left()));
    Local<Type> right(zone_, visitForLoad(node->right()));
    if (!left || !right)
        return;

    Local<Type> result(zone_, typeCheckBinary(node, node->token(), left, right));
    if (!result)
        return;

    node->setType(result);
}

void
SemanticAnalysis::visit(ReturnStatement *node)
{
    // Set this early, so if we error we don't get more errors.
    control_->returns(Returns_Always);

    if (!node->expression()) {
        if (!function_->type()->returnType()->isVoid())
            cc_.reportError(node->pos(), Message_NotAllPathsReturnValue);
        return;
    }

    Local<Type> type(zone_, visitForLoad(node->expression()));
    if (!type)
        return;

    Local<Type> returnType(zone_, function_->type()->returnType());
    if (!typeCheckAssignment(node->pos(), type, returnType, true))
        return;
}

void
SemanticAnalysis::visit(ForStatement *node)
{
    LoopScope loop(&loop_);

    if (node->scope())
        scope_ = node->scope();

    if (node->initialization())
        node->initialization()->accept(this);

    if (node->condition()) {
        Local<Type> type(zone_, visitForLoad(node->condition()));
        if (!type || !canTest(node->condition(), type)) {
            return;
        }
    }

    if (node->update())
        node->update()->accept(this);

    ControlStatus status = visitForControl(node->body());

    // If there is a condition, we treat this like a while loop. Otherwise,
    // join point is only reachable if the loop contains a break. The
    // termination status doesn't tell us anything yet.
    if (node->condition() || loop.breaks())
        control_->merge(status, ControlStatus(false, Returns_Never));
    else
        control_->terminate(status.returns());

    if (node->scope())
        scope_ = scope_->enclosing();
}

void 
SemanticAnalysis::visit(WhileStatement *node)
{
    LoopScope loop(&loop_);

    Local<Type> type(zone_, visitForLoad(node->condition()));
    if (!type || !canTest(node->condition(), type)) {
        return;
    }

    ControlStatus status = visitForControl(node->body());

    // Detect loops that will always have a fork in the control-flow graph.
    // This will be a |while| loop, or a |do-while| loop whose body does
    // not terminate.
    //
    // For now, we treat these as an |if| regardless of the direction the
    // branch can take. That means the control-flow is always conditional.
    if (node->token() == TOK_WHILE ||
        !status.terminates() ||
        loop.breaks())
    {
        control_->merge(status, ControlStatus(false, Returns_Never));
    } else {
        // At this point, we've got a do-while loop with a |return| or
        // |continue|, meaning that the rest of the block is unreachable.
        control_->terminate(status.returns());
    }
}

void
SemanticAnalysis::visit(BlockStatement *node)
{
    if (node->scope())
        scope_ = node->scope();
    for (size_t i = 0; i < node->statements()->length(); i++) {
        Statement *statement = node->statements()->at(i);
        statement->accept(this);
    }
    if (node->scope())
        scope_ = scope_->enclosing();
}

static inline bool
IsScalarString(Type *type)
{
    if (type->isPrimitive() && type->primitive() == PrimitiveType_Char)
        return true;
    if (type->isReference())
        return IsScalarString(type->contained());
    return false;
}

static const int INFER_ARRAY_SIZE = 0;
static const int EVAL_ARRAY_SIZE = -1;
static const int DYNAMIC_ARRAY_SIZE = -2;

bool
SemanticAnalysis::evaluateDimensions(ExpressionList *exprs, int dims[MAX_ARRAY_DEPTH])
{
    bool hadEmptySize = false;

    for (size_t i = 0; i < exprs->length(); i++) {
        Expression *expr = exprs->at(i);
        if (!expr) {
            dims[i] = INFER_ARRAY_SIZE;
            hadEmptySize = true;
            continue;
        }

        BoxedPrimitive box;
        if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) {
            int size = box.toInt();
            if (size <= 0) {
                cc_.reportError(expr->pos(), Message_BadArraySize);
                return false;
            }

            dims[i] = size;
            continue;
        }

        Local<Type> exprType(zone_, visitForLoad(expr));
        if (!exprType)
            return false;

        if (!IsPrimitiveType(exprType, PrimitiveType_Int32)) {
            cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger);
            return false;
        }

        if (hadEmptySize) {
            cc_.reportError(expr->pos(), Message_BadDynamicInitializer);
            return false;
        }

        dims[i] = EVAL_ARRAY_SIZE;
    }

    return true;
}

bool
SemanticAnalysis::inferFixedArrayLiteral(ArrayLiteral *literal, Handle<Type> contained,
                                         int dims[MAX_ARRAY_DEPTH],
                                         unsigned dimcount, unsigned level)
{
    if (dims[level] == EVAL_ARRAY_SIZE) {
        cc_.reportError(literal->pos(), Message_CannotInitializeEvalArray);
        return false;
    }

    if (dims[level] == INFER_ARRAY_SIZE) {
        // Strings always become dynamic arrays.
        if (level != dimcount - 1 || !IsPrimitiveType(contained, PrimitiveType_Char))
            dims[level] = literal->expressions()->length();
    } else if (dims[level] == DYNAMIC_ARRAY_SIZE) {
        cc_.reportError(literal->pos(), Message_CannotInitializeEvalArray);
        return false;
    } else {
        if (literal->expressions()->length() != unsigned(dims[level])) {
            cc_.reportError(literal->pos(), Message_ArrayDimensionSizeMismatch, level + 1);
            return false;
        }
    }

    if (level == dimcount - 2 && IsPrimitiveType(contained, PrimitiveType_Char)) {
        for (size_t i = 0; i < literal->expressions()->length(); i++) {
            Expression *expr = literal->expressions()->at(i);
            if (!expr->isStringLiteral()) {
                cc_.reportError(expr->pos(), Message_ExpectedStringLiteral);
                return false;
            }

            StringLiteral *lit = expr->toStringLiteral();
            if (dims[level + 1] > 0 &&
                unsigned(dims[level]) < lit->string()->length())
            {
                cc_.reportError(lit->pos(), Message_ArrayDimensionSizeMismatch, level + 2);
                return false;
            }
            if (!visitAndApplyType(lit))
                return false;
        }
    } else if (level == dimcount - 1) {
        Local<Type> type(zone_);
        for (size_t i = 0; i < literal->expressions()->length(); i++) {
            Expression *expr = literal->expressions()->at(i);
            if (expr->isArrayLiteral()) {
                cc_.reportError(expr->pos(), Message_ArrayDimensionsMismatch);
                return false;
            }

            if ((type = visitAndApplyType(expr)) == NULL)
                return false;

            if (!typeCheckAssignment(expr->pos(), type, contained, true))
                return false;
        }
    } else {
        for (size_t i = 0; i < literal->expressions()->length(); i++) {
            Expression *expr = literal->expressions()->at(i);
            if (!expr->isArrayLiteral()) {
                cc_.reportError(expr->pos(), Message_ArrayDimensionsMismatch);
                return false;
            }

            assert(expr->toArrayLiteral()->isFixed());

            if (!inferFixedArrayLiteral(expr->toArrayLiteral(),
                                        contained,
                                        dims,
                                        dimcount,
                                        level + 1))
            {
                return false;
            }
        }
    }

    return true;
}

void
SemanticAnalysis::visit(VariableDeclaration *node)
{
    Local<Type> type(zone_, bindType(node->pos(), node->type()));
    if (!type)
        return;

    if (node->postDimensions() || type->isArray()) {
        // The type could be an array type, so sum the type and postDimensions
        // together.
        unsigned dimcount = node->postDimensions()->length();
        if (type->isArray())
            dimcount += type->levels();
        if (dimcount > MAX_ARRAY_DEPTH) {
            cc_.reportError(node->pos(), Message_MaximumArrayDepth);
            return;
        }

        // Take an initial guess at the array dimension sizes.
        int dims[MAX_ARRAY_DEPTH];
        if (!evaluateDimensions(node->postDimensions(), dims))
            return;

        // Now add in the type's array dimensions.
        Local<Type> contained(zone_, type);
        for (size_t i = node->postDimensions()->length(); i < dimcount; i++) {
            if (type->isFixedLength())
                dims[i] = type->fixedLength();
            else
                dims[i] = DYNAMIC_ARRAY_SIZE;
            contained = contained->contained();
        }

        Expression *initializer = node->initialization();
        if (initializer &&
            initializer->isArrayLiteral() &&
            initializer->toArrayLiteral()->isFixed())
        {
            // Fixed array initializers are special; we use them to infer any
            // missing information in the original array. We do not evaluate
            // the array initializer as an expression.
            if (!inferFixedArrayLiteral(initializer->toArrayLiteral(),
                                        contained,
                                        dims,
                                        dimcount,
                                        0))
            {
                return;
            }
            initializer = NULL;
        }

        // We now have enough information to build the actual array type.
        // Start from the original dimcount, not the one computed for
        // initializers.
        for (size_t i = node->postDimensions()->length() - 1;
             i < node->postDimensions()->length();
             i--)
        {
            int size = dims[i] > 0
                       ? dims[i]
                       : Type::DYNAMIC_ARRAY_SIZE;

            type = zone_->types()->newArray(type, size, false);
            if (!type)
                return;
        }

        // Finally, if we had a non-fixed initializer, make sure it type
        // checks. 
        if (initializer) {
            Local<Type> right(zone_, visitForLoad(initializer));
            if (!right)
                return;

            if (!typeCheckAssignment(node->pos(), right, type, true))
                return;
        }
    } else {
        if (node->initialization()) {
            Local<Type> right(zone_, visitForLoad(node->initialization()));
            if (!right)
                return;
            if (!typeCheckAssignment(node->pos(), right, type, true))
                return;
        }

        if (type->isPrimitive() && type->primitive() == PrimitiveType_Char) {
            cc_.reportError(node->pos(), Message_CannotDeclareStringCell);
            return;
        }
    }

    // If this variable is stack allocated, we could use the LIFO heap
    // to allocate it. If that's the case, make sure to reserve a slot
    // so we can unwind the LIFO heap at the end of this scope.
    if (!node->variable()->needsHeapAllocation()) {
        if (type->isArray() || type->isStruct())
            scope_->setUsesLifoHeap();
    }

    node->variable()->setType(type);
}

void
SemanticAnalysis::visit(IntegerLiteral *node)
{
    node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32));
}

void
SemanticAnalysis::visit(BooleanLiteral *node)
{
    node->setType(zone_->types()->getPrimitive(PrimitiveType_Bool));
}

void
SemanticAnalysis::visit(ExpressionStatement *node)
{
    visitAndApplyType(node->expression());
}

Box *
SemanticAnalysis::evaluateDefaultParameter(Handle<Type> type, Parameter *parameter)
{
    Local<Type> given(zone_, visitForLoad(parameter->initializer()));

    BoxedPrimitive prim;
    if (EvaluateForConstant(parameter->initializer(), &prim)) {
        if (!typeCheckAssignment(parameter->initializer()->pos(), given, type, false))
            return NULL;

        if (prim.type() == PrimitiveType_Int32)
            return Box::New(zone_, given, prim.toInt());
        return Box::New(zone_, given, prim.toFloat());
    } else if (parameter->initializer()->isStringLiteral()) {
        StringLiteral *literal = parameter->initializer()->toStringLiteral();

        if (!typeCheckAssignment(parameter->initializer()->pos(), given, type, false))
            return NULL;

        Local<ArrayMap> map(zone_, ArrayMap::Attach(zone_, given));
        if (!map)
            return NULL;

        Local<Array> array(zone_,
            Array::NewString(zone_,
                             map,
                             literal->string()->raw(),
                             literal->string()->length(),
                             Heap::Tenure_Old));
        if (!array)
            return NULL;

        return Box::NewObj(zone_, given, array, Heap::Tenure_Old);
    }

    cc_.reportError(parameter->initializer()->pos(), Message_MustBeConstant);
    return NULL;
}

bool
SemanticAnalysis::buildFunctionArgs(AstNode *node, TokenKind kind, const FunctionSignature &sig,
                                    FixedArray **argsp, FixedArray **defaultsp)
{
    Local<FixedArray> args(zone_, FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old));
    if (!args)
        return false;

    Local<FixedArray> defaults(zone_);

    // Apply types to arguments.
    for (size_t i = 0; i < sig.parameters()->length(); i++) {
        Parameter *parameter = sig.parameters()->at(i);
        
        Local<Type> type(zone_, bindType(parameter->variable()->pos(), parameter->type()));
        if (!type)
            return false;

        if (parameter->reference()) {
            if (parameter->dimensions()) {
                cc_.reportError(parameter->variable()->pos(), Message_ArrayCantBeReference);
                return false;
            }

            if (type->isStruct()) {
                cc_.reportError(parameter->variable()->pos(), Message_StructCantBeReference);
                return false;
            }

            type = zone_->types()->getReference(type, parameter->isConst());
            if (!type)
                return false;
        } else if (parameter->dimensions()) {
            type = buildArrayType(type, parameter->dimensions(), parameter->isConst());
            if (!type)
                return false;

            if (kind == TOK_NATIVE && type->levels() > 1) {
                cc_.reportError(parameter->variable()->pos(), Message_MultiArrayInNative);
                return false;
            }
        }

        if (IsScalarString(type)) {
            cc_.reportError(node->pos(), Message_CannotDeclareStringCell);
            return false;
        }

        args->set(zone_, i, type);
        parameter->variable()->setType(type);

        if (parameter->initializer()) {
            if (!defaults) {
                defaults = FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old);
                if (!defaults)
                    return false;
            }

            Local<Box> box(zone_, evaluateDefaultParameter(type, parameter));
            if (!box)
                return false;

            defaults->set(zone_, i, box);
        }
    }

    *argsp = args;
    *defaultsp = defaults;

    return true;
}

void
SemanticAnalysis::visit(FunctionTypeStatement *node)
{
    if (node->decl()->type())
        return;

    Local<FixedArray> args(zone_);
    Local<FixedArray> defaults(zone_);

    const FunctionSignature &sig= node->signature();
    if (!buildFunctionArgs(node,
                           TOK_STOCK,
                           sig,
                           args.address(),
                           defaults.address()))
    {
        return;
    }

    Local<Type> returnType(zone_);
    if (sig.returnType())
        returnType = bindType(node->pos(), sig.returnType());
    else
        returnType = zone_->types()->getPrimitive(PrimitiveType_Int32);

    if (!returnType)
        return;

    Local<Type> funtype(zone_, zone_->types()->newFunction(returnType, args, defaults));
    if (!funtype)
        return;

    node->decl()->setType(funtype);
}

Type *
SemanticAnalysis::buildArrayType(Handle<Type> type, ExpressionList *dimensions, bool isConst)
{
    Local<Type> arrayType(zone_, type);

    unsigned dimcount = dimensions->length();
    if (type->isArray())
        dimcount += type->levels();

    if (dimcount > MAX_ARRAY_DEPTH) {
        cc_.reportError(dimensions->at(dimensions->length() - 1)->pos(),
                        Message_MaximumArrayDepth);
        return false;
    }

    // Note: constness is sort of bogus. Fix this sometime.
    for (size_t i = dimensions->length() - 1; i < dimensions->length(); i--) {
        Expression *expr = dimensions->at(i);
        if (!expr) {
            arrayType = zone_->types()->newArray(arrayType, Type::DYNAMIC_ARRAY_SIZE, isConst);
            if (!arrayType)
                return NULL;

            continue;
        }

        BoxedPrimitive box;
        if (!EvaluateForConstant(expr, &box) || box.type() != PrimitiveType_Int32) {
            cc_.reportError(expr->pos(), Message_ArraySizeMustBeConstant);
            return NULL;
        }

        if (box.toInt() <= 0) {
            cc_.reportError(expr->pos(), Message_BadArraySize);
            return NULL;
        }

        arrayType = zone_->types()->newArray(arrayType, box.toInt(), isConst);
        if (!arrayType)
            return NULL;
    }

    return arrayType;
}

bool
SemanticAnalysis::preanalyze(FunctionStatement *node)
{
    Local<FixedArray> args(zone_);
    Local<FixedArray> defaults(zone_);

    const FunctionSignature &sig = node->signature();
    if (!buildFunctionArgs(node,
                           node->fun()->token(),
                           sig,
                           args.address(),
                           defaults.address()))
    {
        return false;
    }

    Local<Type> returnType(zone_);
    if (sig.returnType() ||
        node->hasExplicitReturn() ||
        node->fun()->isNative())
    {
        returnType = bindType(node->pos(), sig.returnType());
    } else {
        returnType = zone_->types()->getVoid();
    }
    if (!returnType)
        return false;

    if (sig.returnDimensions()) {
        returnType = buildArrayType(returnType, sig.returnDimensions(), false);
        if (!returnType)
            return false;
    }

    if (IsScalarString(returnType)) {
        cc_.reportError(node->pos(), Message_CannotDeclareStringCell);
        return false;
    }

    Local<Type> funtype(zone_);
    if (node->fun()->isNative())
        funtype = zone_->types()->newNative(returnType, args, defaults, node->fun()->isNativeVariadic());
    else
        funtype = zone_->types()->newFunction(returnType, args, defaults);
    if (!funtype)
        return false;
    
    // If the function already has a type, it was forward declared before, and
    // now this is our chance to type check the second declaration.
    if (node->fun()->type()) {
        Local<Type> forward(zone_, node->fun()->type());
        if (!Type::Compare(funtype, forward)) {
            cc_.reportError(node->pos(), Message_ForwardDeclareMismatches);
            return false;
        }
        node->fun()->setForwardType(forward);
    }

    node->fun()->setType(funtype);

    return true;
}

void
SemanticAnalysis::visit(FunctionStatement *node)
{
    function_ = node->fun();
    scope_ = node->scope();

    ControlScope control(&control_);
    if (node->body()) {
        node->body()->accept(this);

        ControlStatus status = control.status();
        if (status.returns() == Returns_Sometimes) {
            cc_.reportError(node->pos(), Message_NotAllPathsReturnValue);
        } else if (status.returns() == Returns_Never) {
            if (!function_->type()->returnType()->isVoid())
                cc_.reportError(node->pos(), Message_MissingReturnValue);
        }
    }

    scope_ = NULL;
    function_ = NULL;
}

void
SemanticAnalysis::typeMismatch(const SourcePosition &pos, Handle<Type> actual, Handle<Type> given)
{
    cc_.reportError(pos, Message_TypeMismatch,
                    GetTypeName(actual),
                    GetTypeName(given));
}

bool
SemanticAnalysis::typeCheckArrayAssignment(const SourcePosition &pos, Handle<Type> argGiven,
                                           Handle<Type> argActual, bool forAssign)
{
    Local<Type> actual(zone_, argActual);
    Local<Type> given(zone_, argGiven);

    if (!given->isArray()) {
        cc_.reportError(pos, Message_ExpectedArray);
        return false;
    }
    if (given->levels() != actual->levels()) {
        cc_.reportError(pos, Message_ArrayDimensionsMismatch);
        return false;
    }

    unsigned level = 1;
    while (given->isArray()) {
        if (actual->isFixedLength()) {
            if (forAssign) {
                if (!given->isFixedLength()) {
                    cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level);
                    return false;
                }

                if (actual->isCharArray()) {
                    // String assignment can have a truncated |given|.
                    if (given->fixedLength() > actual->fixedLength()) {
                        cc_.reportError(pos, Message_ArrayAssignmentTooBig);
                        return false;
                    }
                } else {
                    if (given->fixedLength() != actual->fixedLength()) {
                        cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level);
                        return false;
                    }
                }
            }
        } else if (given->isFixedLength()) {
            // We do not allow assignments in the form:
            //    [][] = [][Y]
            //
            // Otherwise, the following code could break the size guarantees
            // of the right-hand side:
            //
            //   new x[][5];
            //   new y[][];
            //   new z[3];
            //
            //   y = x;
            //   y[0] = z;
            //   
            // Now |x[0]| contains a 4-length array.
            if (level > 1) {
                cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level);
                return false;
            }
        }
        given = given->contained();
        actual = actual->contained();
        level++;
    }

    return typeCheckAssignment(pos, given, actual, forAssign);
}

bool
SemanticAnalysis::typeCheckAssignment(const SourcePosition &pos, Handle<Type> argGiven,
                                      Handle<Type> argActual, bool forAssign)
{
    Local<Type> actual(zone_, argActual);
    Local<Type> given(zone_, argGiven);

    if (actual->isArray())
        return typeCheckArrayAssignment(pos, given, actual, forAssign);

    if (given->isArray()) {
        cc_.reportError(pos, Message_CannotAssignArrayToScalar);
        return false;
    }

    // The given type may not have been dereferenced.
    if (given->isReference())
        given = given->contained();
    if (actual->isReference())
        actual = actual->contained();

    // We coerce unchecked to a few things.
    if (actual->isUnchecked() || given->isUnchecked()) {
        Type *check = actual->isUnchecked() ? given : actual;
        if (check->isUnchecked() ||
            check->isEnum() ||
            check->isInt32() ||
            check->isBool() ||
            check->isFloat())
        {
            return true;
        }
        typeMismatch(pos, actual, given);
        return false;
    }

    if (given->isFunction() && actual->isFunction()) {
        if (!Type::Compare(given, actual)) {
            typeMismatch(pos, actual, given);
            return false;
        }
        return true;
    }

    if (actual->isEnum()) {
        if (!given->isEnum() || actual != given) {
            typeMismatch(pos, actual, given);
            return false;
        }
        return true;
    }

    if (actual->isStruct()) {
        if (!given->isStruct() || actual != given) {
            typeMismatch(pos, actual, given);
            return false;
        }
        return true;
    }

    if (given->isEnum() || given->isFunction() || given->isStruct()) {
        typeMismatch(pos, actual, given);
        return false;
    }

    // This coercion is trivial.
    if (given->isBool() && actual->isInt32())
        return true;

    if (given->primitive() != actual->primitive()) {
        typeMismatch(pos, actual, given);
        return false;
    }

    return true;
}

bool
SemanticAnalysis::typeCheckParameter(AstNode *node, Handle<Type> given, Handle<Type> actual)
{
    return typeCheckAssignment(node->pos(), given, actual, false);
}

bool
SemanticAnalysis::checkArgumentCount(Handle<Type> type, unsigned actual)
{
    if (type->parameters()->length() == actual)
        return true;

    if (actual > type->parameters()->length()) {
        if (!type->isNative() || !type->isNativeVariadic())
            return false;
        return true;
    }

    if (!type->defaults())
        return false;

    // Missing arguments. See if there's a default argument for each missing
    // actual.
    for (unsigned i = actual; i < type->parameters()->length(); i++) {
        if (!type->defaults()->at(i))
            return false;
    }

    return true;
}

void
SemanticAnalysis::visit(CallExpression *node)
{
    Local<Type> type(zone_, visitForLoad(node->callee()));
    if (!type)
        return;

    if (!type->isFunction()) {
        cc_.reportError(node->pos(), Message_CalleeNotFunction);
        return;
    }

    if (!checkArgumentCount(type, node->arguments()->length())) {
        cc_.reportError(node->pos(), Message_ArgumentCountMismatch);
        return;
    }

    // NamedFunction *fun = node->callee()->toNameProxy()->binding()->toNamedFunction();
    // if (fun->isForward()) {
    //     cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars());
    //     return;
    // }

    for (unsigned i = 0; i < node->arguments()->length(); i++) {
        // Note: we don't compute references unless the callee needs a
        // reference type.
        Expression *expression = node->arguments()->at(i);
        Local<Type> given(zone_, visitAndApplyType(expression));
        if (!given)
            return;

        // Since we skip a load, check for a call expression that returns void.
        if (given->isVoid()) {
            assert(expression->isCallExpression());
            CallExpression *call = expression->toCallExpression();
            cc_.reportError(call->callee()->pos(), Message_FunctionDoesNotReturnValue);
            return;
        }
        
        bool mustComputeReference;
        if (i >= type->parameters()->length()) {
            // Special handling for natives. Arguments past the formal must
            // always have references computed. However, arrays are already
            // references for natives.  
            assert(type->isNative() && type->isNativeVariadic());
            mustComputeReference = !given->isReference() && !given->isArray();
        } else {
            Local<Type> actual(zone_, type->parameterAt(i));

            if (expression->isIndexExpression()) {
                // Disabled until we can make it work.
#if 0
                if (actual->isArray()) {
                    // We special case the ability to pass x[y] into x[], which
                    // creates an array slice. We only do this if we know the
                    // assignment would otherwise not type-check.
                    IndexExpression *index = expression->toIndexExpression();
                    if (index->left()->type()->levels() == actual->levels())
                        given = index->left()->type();
                } else
#endif
                if (actual->isReference()) {
                    cc_.reportError(expression->pos(), Message_CannotComputeIndexRef);
                    return;
                }
            }

            if (!typeCheckParameter(expression, given, actual))
                return;
            mustComputeReference = actual->isReference() && !given->isReference();
        }

        // If the actual is a reference, and the given is not, we'll need to
        // compute a reference, which may be allocated on the stack.
        if (mustComputeReference)
            scope_->setUsesLifoHeap();
    }

    node->setType(type->returnType());
}

void
SemanticAnalysis::ControlScope::inherit(const ControlStatus &other)
{
    if (other.returns() < status().returns())
        return;

    if (other.terminates())
        terminate(other.returns());
    else
        returns(other.returns());
}

void
SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right)
{
    // If both sides terminate in a return, we consider this a return as well.
    if (left.returns() == Returns_Always && right.returns() == Returns_Always) {
        terminate(Returns_Always);
        return;
    }

    ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never)
                          ? Returns_Never
                          : Returns_Sometimes;

    if (left.terminates() && right.terminates())
        terminate(status);
    else
        returns(status);
}

void
SemanticAnalysis::visit(IfStatement *node)
{
    Local<Type> type(zone_, visitForLoad(node->condition()));
    if (!type)
        return;

    if (!canTest(node, type))
        return;

    ControlStatus trueBranch = visitForControl(node->ifTrue());
    ControlStatus falseBranch = ControlStatus(false, Returns_Never);

    if (node->ifFalse())
        falseBranch = visitForControl(node->ifFalse());

    control_->merge(trueBranch, falseBranch);
}

void
SemanticAnalysis::visit(IndexExpression *node)
{
    Local<Type> left(zone_, visitForLoad(node->left()));
    Local<Type> right(zone_, visitForLoad(node->right()));

    if (!left || !right)
        return;

    if (!right->isInt32() && !right->isEnum()) {
        cc_.reportError(node->right()->pos(), Message_IndexMustBeInteger);
        return;
    }
    if (!left->isArray()) {
        cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray);
        return;
    }

    // Note: char arrays are automatically up-casted to return an int32.
    if (left->isCharArray())
        node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32));
    else
        node->setType(left->contained());
}

void
SemanticAnalysis::visit(FieldExpression *node)
{
    Local<Type> left(zone_, visitForLoad(node->left()));
    if (!left)
        return;

    if (!left->isStruct()) {
        cc_.reportError(node->pos(), Message_PropertyNotFound, node->field()->chars(),
                        GetTypeName(left));
        return;
    }

    Local<String> field(zone_, node->field());

    unsigned offset;
    Local<Descriptor> desc(zone_, left->lookupField(field, &offset));
    if (!desc) {
        cc_.reportError(node->pos(), Message_PropertyNotFound, node->field()->chars(),
                        GetTypeName(left));
        return;
    }

    node->setFieldIndex(offset);
    node->setType(desc->type());
}

void
SemanticAnalysis::visit(EnumStatement *node)
{
    Local<String> name(zone_, node->name());
    Local<Type> type(zone_);
    if (name)
        type = zone_->types()->newEnum(name);
    else
        type = zone_->types()->getPrimitive(PrimitiveType_Int32);

    if (!type)
        return;

    if (node->decl())
        node->decl()->setType(type);

    int value = 0;
    for (size_t i = 0; i < node->entries()->length(); i++) {
        EnumStatement::Entry &entry = node->entries()->at(i);

        BoxedPrimitive box = BoxedPrimitive::Int(value);
        if (entry.expr) {
            entry.expr->accept(this);
            if (!EvaluateForConstant(entry.expr, &box) || box.type() != PrimitiveType_Int32) {
                cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant);
                return;
            }

            assert(box.type() == PrimitiveType_Int32);
            value = box.toInt();
        }

        entry.name->set(type, box);
        value++;
    }
}

static int
SortCaseIntValues(const void *p1, const void *p2)
{
    CaseValue *v1 = (CaseValue *)p1;
    CaseValue *v2 = (CaseValue *)p2;

    return v1->box.toInt() - v2->box.toInt();
}

void
SemanticAnalysis::visit(SwitchStatement *node)
{
    Local<Type> caseType(zone_, visitForLoad(node->expression()));
    if (!caseType)
        return;

    if (!caseType->isEnum() && !caseType->isInt32()) {
        cc_.reportError(node->pos(), Message_CaseMustBeInteger);
        return;
    }

    PoolList<CaseValue> *values = new (zone_->pool()) PoolList<CaseValue>();
    ControlStatus status = ControlStatus(false, Returns_Never);

    Local<Type> check(zone_);
    for (size_t i = 0; i < node->cases()->length(); i++) {
        Case *c = node->cases()->at(i);

        check = visitForLoad(c->expression());
        if (!check)
            return;

        if (!typeCheckAssignment(c->expression()->pos(), check, caseType, false))
            return;

        CaseValue v(i);
        if (!EvaluateForConstant(c->expression(), &v.box)) {
            cc_.reportError(c->expression()->pos(), Message_CaseMustBeConstant);
            return;
        }
        if (!values->append(v))
            return;

        if (c->others()) {
            for (size_t j = 0; j < c->others()->length(); j++) {
                Expression *expr = c->others()->at(j);
                check = visitForLoad(expr);
                if (!check)
                    return;

                if (!typeCheckAssignment(expr->pos(), check, caseType, false))
                    return;

                CaseValue v(i);
                if (!EvaluateForConstant(expr, &v.box)) {
                    cc_.reportError(expr->pos(), Message_CaseMustBeConstant);
                    return;
                }
                if (!values->append(v))
                    return;
            }
        }

        ControlStatus latest = visitForControl(c->statement());

        if (i != 0)
            control_->merge(status, latest);

        status = latest;
    }

    if (node->defaultCase()) {
        ControlStatus latest = visitForControl(node->defaultCase());

        if (node->cases()->length()) {
            // If we had any other cases, we must merge.
            control_->merge(status, latest);
        } else {
            // Otherwise, control-flow always flows through here.
            control_->inherit(latest);
        }
    } else if (node->cases()->length()) {
        control_->merge(status, ControlStatus(false, Returns_Never));
    }

    if (values->length() > 1) {
        qsort(values->buffer(), values->length(), sizeof(CaseValue), SortCaseIntValues);

        for (size_t i = 1; i < values->length(); i++) {
            CaseValue &prev = values->at(i - 1);
            CaseValue &cur = values->at(i);
            if (prev.box.toInt() == cur.box.toInt()) {
                cc_.reportError(node->cases()->at(cur.statement)->expression()->pos(), Message_CaseHasSameValue);
                return;
            }
        }
    }

    node->setCaseValueList(values);
}

void
SemanticAnalysis::visit(StructureStatement *node)
{
    // If this node already has a type, it was pre-analyzed, so skip it.
    if (node->decl()->type())
        return;

    FieldList *fields = node->fields();
    Local<FixedArray> desctable(zone_, FixedArray::New(zone_, fields->length(), Heap::Tenure_Old));
    if (!desctable)
        return;

    Vector<unsigned> composedList;
    StructureBufferBuilder builder;

    Local<Type> type(zone_);
    Local<String> name(zone_);
    Local<Descriptor> desc(zone_);
    for (size_t i = 0; i < fields->length(); i++) {
        StructureStatement::Field *field = fields->at(i);

        type = bindType(field->pos(), field->type());
        if (!type)
            return;

        if (type->isPrimitive() && type->primitive() == PrimitiveType_Char) {
            cc_.reportError(field->pos(), Message_CannotDeclareStringCell);
            return;
        }

        unsigned offset;
        if (!builder.add(type, &offset))
            return;

        name = field->name();
        desc = Descriptor::New(zone_, name, type, offset);
        if (!desc)
            return;

        desctable->set(zone_, i, desc);

        if (type->isStruct() || type->isArray()) {
            if (!composedList.append(i))
                return;
        }
    }

    Local<FixedArray> composed(zone_);
    if (composedList.length()) {
        composed = FixedArray::New(zone_, composedList.length(), Heap::Tenure_Old);
        if (!composed)
            return;

        for (unsigned i = 0; i < composedList.length(); i++)
            composed->set(zone_, i, desctable->at(i));
    }

    Local<StructMap> map(zone_, StructMap::New(zone_, builder, composed));
    if (!map)
        return;

    name = node->decl()->name();
    type = zone_->types()->newStruct(name, desctable, map);
    if (!type)
        return;

    map->setType(type);
    node->decl()->setType(type);
}

void
SemanticAnalysis::visit(StringLiteral *node)
{
    Local<Type> type(zone_, zone_->types()->getPrimitive(PrimitiveType_Char));
    if (!type)
        return;

    type = Type::NewArray(zone_, type, node->string()->length() + 1, true);
    if (!type)
        return;

    node->setType(type);
}

void
SemanticAnalysis::visit(FloatLiteral *node)
{
    Local<Type> type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float));
    if (!type)
        return;

    node->setType(type);
}

void
SemanticAnalysis::visit(BreakStatement *node)
{
    if (!loop_) {
        cc_.reportError(node->pos(), Message_BreakOutsideLoop);
        return;
    }

    // Note: ignore breaks that occur in unreachable control-flow.
    if (!control_->status().terminates())
        loop_->addBreak();

    control_->terminate(Returns_Never);
}

void
SemanticAnalysis::visit(ContinueStatement *node)
{
    if (!loop_) {
        cc_.reportError(node->pos(), Message_ContinueOutsideLoop);
        return;
    }

    // Note: ignore continues that occur in unreachable control-flow.
    if (!control_->status().terminates())
        loop_->addContinue();

    control_->terminate(Returns_Never);
}

void
SemanticAnalysis::visit(ArrayLiteral *node)
{
    // We should never visit this.
    cc_.reportError(node->pos(), Message_UnexpectedArrayLiteral);
}

