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