diff -r b029d63565a2 -r c1ba166f3fc4 src/OldSema.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/OldSema.cpp Sat Nov 17 18:41:01 2012 -0800 @@ -0,0 +1,1899 @@ +/* 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 +#include "SemanticAnalysis.h" +#include "../Types.h" +#include "../Zone.h" +#include "../TypeManager.h" +#include "../Strings.h" +#include "../Box.h" +#include "../Array.h" +#include "../Heap-inl.h" +#include "Scopes.h" +#include "NameBinding.h" + +using namespace ke; + +SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree) + : zone_(zone), + cc_(cc), + tree_(tree), + scope_(NULL), + loop_(NULL) +{ +} + +bool +SemanticAnalysis::analyze() +{ + assert(false); + return false; +} + + +#if 0 + +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_->globalScope(); + assert(scope_->kind() == Scope::GLOBAL); + + 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 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(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(); +} + +void +SemanticAnalysis::unaryTypeError(AstNode *node, TokenKind token, Handle 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 left, Handle 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 left, Handle 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) +{ + 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 exprType(zone_, visitForLoad(node->expression())); + if (!exprType) + return ; + + Local 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(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(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 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; + } + + case TOK_SIZEOF: + { + if (!type->isArray()) { + cc_.reportError(node->pos(), Message_SizeofNeedsArray); + return; + } + node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32)); + 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(zone_, visitForLoad(node->condition())); + if (!type) + return; + + if (!canTest(node->condition(), type)) + return; + + Local left(zone_, visitForLoad(node->left())); + Local 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 left(zone_, visitForLoad(node->left())); + Local right(zone_, visitForLoad(node->right())); + if (!left || !right) + return; + + Local 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(zone_, visitForLoad(node->expression())); + if (!type) + return; + + Local 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(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(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 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 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(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(zone_, bindType(node->pos(), node->type())); + if (!type) + return; + +#if 0 + 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 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 right(zone_, visitForLoad(initializer)); + if (!right) + return; + + if (!typeCheckAssignment(node->pos(), right, type, true)) + return; + } + } else { + if (node->initialization()) { + Local 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; + } + } +#endif + +#if 0 + // 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); +#endif +} + +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, Parameter *parameter) +{ + Local 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 map(zone_, ArrayMap::Attach(zone_, given)); + if (!map) + return NULL; + + Local 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 args(zone_, FixedArray::New(zone_, sig.parameters()->length(), Heap::Tenure_Old)); + if (!args) + return false; + + Local defaults(zone_); + + // Apply types to arguments. + for (size_t i = 0; i < sig.parameters()->length(); i++) { + Parameter *parameter = sig.parameters()->at(i); + +#if 0 + Local 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(zone_, evaluateDefaultParameter(type, parameter)); + if (!box) + return false; + + defaults->set(zone_, i, box); + } +#endif + } + + *argsp = args; + *defaultsp = defaults; + + return true; +} + +void +SemanticAnalysis::visit(FunctionTypeStatement *node) +{ + if (node->decl()->type()) + return; + + Local args(zone_); + Local defaults(zone_); + + const FunctionSignature &sig= node->signature(); + if (!buildFunctionArgs(node, + TOK_STOCK, + sig, + args.address(), + defaults.address())) + { + return; + } + +#if 0 + Local returnType(zone_); + if (sig.returnType()) + returnType = bindType(node->pos(), sig.returnType()); + else + returnType = zone_->types()->getPrimitive(PrimitiveType_Int32); + + if (!returnType) + return; + + Local funtype(zone_, zone_->types()->newFunction(returnType, args, defaults)); + if (!funtype) + return; + + node->decl()->setType(funtype); +#endif +} + +Type * +SemanticAnalysis::buildArrayType(Handle type, ExpressionList *dimensions, bool isConst) +{ + Local 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 args(zone_); + Local defaults(zone_); + + const FunctionSignature &sig = node->signature(); + if (!buildFunctionArgs(node, + node->fun()->token(), + sig, + args.address(), + defaults.address())) + { + return false; + } + +#if 0 + Local 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 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 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); +#endif + + 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 actual, Handle given) +{ + cc_.reportError(pos, Message_TypeMismatch, + GetTypeName(actual), + GetTypeName(given)); +} + +bool +SemanticAnalysis::typeCheckArrayAssignment(const SourcePosition &pos, Handle argGiven, + Handle argActual, bool forAssign) +{ + Local actual(zone_, argActual); + Local 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 argGiven, + Handle argActual, bool forAssign) +{ + Local actual(zone_, argActual); + Local 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 given, Handle actual) +{ + return typeCheckAssignment(node->pos(), given, actual, false); +} + +bool +SemanticAnalysis::checkArgumentCount(Handle 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(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 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 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(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 left(zone_, visitForLoad(node->left())); + Local 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 left(zone_, visitForLoad(node->left())); + if (!left) + return; + + if (!left->isStruct()) { + cc_.reportError(node->pos(), Message_PropertyNotFound, node->field()->chars(), + GetTypeName(left)); + return; + } + + Local field(zone_, node->field()); + + unsigned offset; + Local 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 name(zone_, node->name()); + Local 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(); + } + +#if 0 + entry.name->set(type, box); +#endif + 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 caseType(zone_, visitForLoad(node->expression())); + if (!caseType) + return; + + if (!caseType->isEnum() && !caseType->isInt32()) { + cc_.reportError(node->pos(), Message_CaseMustBeInteger); + return; + } + + PoolList *values = new (zone_->pool()) PoolList(); + ControlStatus status = ControlStatus(false, Returns_Never); + + Local 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 0 + // If this node already has a type, it was pre-analyzed, so skip it. + if (node->decl()->type()) + return; + + FieldList *fields = node->fields(); + Local desctable(zone_, FixedArray::New(zone_, fields->length(), Heap::Tenure_Old)); + if (!desctable) + return; + + Vector composedList; + StructureBufferBuilder builder; + + Local type(zone_); + Local name(zone_); + Local 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 (field->dimensions()) { + type = buildArrayType(type, field->dimensions(), false); + if (!type) + return; + + if (!ArrayMap::Attach(zone_, type)) + return; + } + + if (IsScalarString(type)) { + 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 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 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); +#endif +} + +void +SemanticAnalysis::visit(StringLiteral *node) +{ + Local 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(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); +} + +void +SemanticAnalysis::visit(ImportStatement *node) +{ +} + +#endif \ No newline at end of file