diff -r 03c6a312213b -r 23e8b3d8e4e6 jitcraft/SemanticAnalysis.cpp --- a/jitcraft/SemanticAnalysis.cpp Sun Jul 01 18:39:11 2012 -0700 +++ b/jitcraft/SemanticAnalysis.cpp Mon Jul 02 01:24:57 2012 -0700 @@ -92,8 +92,9 @@ } } -SemanticAnalysis::SemanticAnalysis(ParseTree *tree) - : tree_(tree), +SemanticAnalysis::SemanticAnalysis(CompileContext &cc, ParseTree *tree) + : cc_(cc), + tree_(tree), loopDepth_(0), scope_(NULL) { @@ -102,35 +103,48 @@ bool SemanticAnalysis::analyze() { + if (cc_.failures()) + return false; + + 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; + } + } + for (size_t i = 0; i < tree_->statements()->length(); i++) { Statement *statement = tree_->statements()->at(i); statement->accept(this); } - return true; + return !cc_.failures(); } -void -SemanticAnalysis::visit(NameProxy *name) +Type * +SemanticAnalysis::visitAndApplyType(Expression *expression) { - name->setType(name->binding()->type()); + expression->accept(this); + return expression->type(); } Type * SemanticAnalysis::visitForLoad(Expression *expression) { - expression->accept(this); - if (!expression->type()) + // :TODO: use Local + Type *type = visitAndApplyType(expression); + if (!type) return NULL; - // :TODO: use Local - Type *type = expression->type(); if (type->isReference()) { expression->setType(type->contained()); } else if (expression->isIndexExpression()) { if (type->isArray()) expression->setType(type->contained()); } + return expression->type(); } @@ -141,94 +155,93 @@ return expression->type(); } -static inline bool -TypeCheck(Type *left, Type *right) +static inline const char * +GetTypeName(Type *type) { - if (!left || !right) - return false; - - if (left->isPrimitive()) { - if (!right->isPrimitive()) - return false; - return left->primitive() == right->primitive(); - } - - return false; + if (type->isArray()) + return "array"; + if (type->isFunction()) + return "function"; + if (type->isReference()) + type = type->contained(); + if (type->tag()) + return type->tag(); + return "int"; } -static inline bool -TypeCheckAssignment(Type *left, Type *right) +void +SemanticAnalysis::unaryTypeError(AstNode *node, TokenKind token, Type *type) { - if (!left || !right) - return false; - - if (left->isPrimitive()) { - if (!right->isPrimitive()) - return false; - return left->primitive() == right->primitive(); - } - - if (left->isArray() != right->isArray()) - return false; + const char *op = TokenNames[token]; + const char *name = GetTypeName(type); - if (left->levels() != 1 || right->levels() != 1) - return false; - - if (left->elements() == -1 || right->elements() == -1) - return false; - - if (!TypeCheckAssignment(left->contained(), right->contained())) - return false; - - if (left->elements() != right->elements()) { - if (left->isCharArray() && (left->elements() >= right->elements())) - return true; - return false; - } - - return false; + cc_.reportError(node->pos(), Message_UnaryTypeError, op, name); } -static inline Type * -TypeCheckBinary(TokenKind token, Type *left, Type *right) +void +SemanticAnalysis::binaryTypeError(AstNode *node, TokenKind token, Type *left, Type *right) { - if (!left || !right) - return NULL; + 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, Type *left, Type *right) +{ if ((token >= TOK_PLUS && token <= TOK_SLASH) || (token >= TOK_ASSIGN_ADD && token <= TOK_ASSIGN_DIV)) { // If either side is a float, the other side must be a float or int. if (left->isFloat() || right->isFloat()) { - assert(left->isFloat() || left->isInt32()); - assert(right->isFloat() || right->isInt32()); - return left->isFloat() ? left : right; + Type *floatType = left->isFloat() ? left : right; + Type *other = left->isFloat() ? right : left; + if (other->isFloat() || (other->isInt32() && !other->tag())) + return other; } } - assert(left->isInt32() && right->isInt32()); - assert(!left->tag() && !right->tag()); + if ((!left->isInt32() || left->tag()) || + (!right->isInt32() || right->tag())) + { + binaryTypeError(node, token, left, right); + return NULL; + } + return left; } +bool +SemanticAnalysis::canTest(AstNode *node, Type *type) +{ + if (type->isArray()) { + cc_.reportError(node->pos(), Message_ArrayIsNotBoolean); + return false; + } + return true; +} + Type * SemanticAnalysis::visitForLValue(Expression *expression) { if (expression->isIndexExpression()) { - expression->accept(this); - if (!expression->type()) + Type *type = visitAndApplyType(expression); + if (!type) return NULL; - return expression->type()->contained(); + + return type->contained(); } if (!expression->isNameProxy()) { - assert(false); + cc_.reportError(expression->pos(), Message_ExpectedLValue); return NULL; } NameProxy *proxy = expression->toNameProxy(); if (!proxy->binding()->kind() == BoundName::VARIABLE) { - fprintf(stderr, "error\n"); + cc_.reportError(expression->pos(), Message_ExpectedLValue); return NULL; } @@ -242,20 +255,32 @@ } void +SemanticAnalysis::visit(NameProxy *name) +{ + name->setType(name->binding()->type()); +} + +void SemanticAnalysis::visit(Assignment *node) { - visitForLoad(node->expression()); + Type *exprType = visitForLoad(node->expression()); + if (!exprType) + return ; // :TODO: use Local Type *type = visitForLValue(node->lvalue()); if (!type) return; - node->setType(type); - if (node->token() != TOK_ASSIGN) - assert(TypeCheckBinary(node->token(), node->type(), node->expression()->type())); + if (node->token() != TOK_ASSIGN) { + if (!typeCheckBinary(node, node->token(), type, exprType)) + return; + } - assert(TypeCheckAssignment(node->type(), node->expression()->type())); + if (!typeCheckAssignment(node, exprType, type, true)) + return; + + node->setType(type); } void @@ -267,13 +292,12 @@ return; assert(!type->isReference()); - if (type->isArray()) { - // can't a[]++ - assert(false); + + if ((!type->isInt32() || (type->isInt32() && type->tag())) && !type->isFloat()) { + unaryTypeError(node, node->token(), type); return; } - assert((type->isInt32() && !type->tag()) || type->isFloat()); node->setType(type); } @@ -283,17 +307,25 @@ // :TODO: use Local Type *left = visitForLoad(node->left()); Type *right = visitForLoad(node->right()); + if (!left || !right) + return; - Type *result = TypeCheckBinary(node->token(), left, right); + Type *result = typeCheckBinary(node, node->token(), left, right); + if (!result) + return; + node->setType(result); } void SemanticAnalysis::visit(ReturnStatement *node) { - visitForLoad(node->expression()); + Type *type = visitForLoad(node->expression()); + if (!type) + return; - // :TODO: type check + if (!typeCheckAssignment(node, type, function_->returnType(), true)) + return; } void @@ -302,13 +334,23 @@ loopDepth_++; if (node->scope()) scope_ = node->scope(); + if (node->initialization()) node->initialization()->accept(this); - if (node->condition()) - visitForLoad(node->condition()); + + if (node->condition()) { + Type *type = visitForLoad(node->condition()); + if (!type || !canTest(node->condition(), type)) { + loopDepth_--; + return; + } + } + if (node->update()) node->update()->accept(this); + node->body()->accept(this); + if (node->scope()) scope_ = scope_->enclosing(); loopDepth_--; @@ -318,7 +360,13 @@ SemanticAnalysis::visit(WhileStatement *node) { loopDepth_++; - node->condition()->accept(this); + + Type *type = visitForLoad(node->condition()); + if (!type || !canTest(node->condition(), type)) { + loopDepth_--; + return; + } + node->body()->accept(this); loopDepth_--; } @@ -347,61 +395,90 @@ if (node->postDimensions()) { Type *init = NULL; if (node->initialization()) { - node->initialization()->accept(this); - if ((init = node->initialization()->type()) == NULL) + if ((init = visitAndApplyType(node->initialization())) == NULL) return; } if (node->postDimensions()->size() > Type::MAX_UNSAFE_ARRAY_DEPTH) { - error(); + cc_.reportError(node->pos(), Message_MaximumArrayDepth); return; } // If we have an initializer, make sure it's the same type. - if (init) { - assert(init->levels() == node->postDimensions()->size()); + if (init && node->postDimensions()->size() != init->levels()) { + cc_.reportError(node->pos(), Message_ArrayInitializerDepth); + return; } Type *check = init; for (size_t i = node->postDimensions()->size() - 1; i < node->postDimensions()->size(); i--) { Expression *expr = node->postDimensions()->at(i); - visitForLoad(expr); + int size = -1; - if (!IsPrimitiveType(expr->type(), PrimitiveType_Int32)) { - typeError(expr, "integer"); - return; - } + if (expr) { + Type *exprType = visitForLoad(expr); + if (!exprType) + return; - int size = -1; - if (expr->isIntegerLiteral()) { - size = expr->toIntegerLiteral()->value(); - if (size <= 0) { - error(); + if (!IsPrimitiveType(exprType, PrimitiveType_Int32)) { + cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger); return; } - if (size >= INT_MAX / sizeof(int)) { - error(); + + BoxedPrimitive box; + if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) { + size = box.toInt(); + if (size <= 0) { + cc_.reportError(expr->pos(), Message_BadArraySize); + return; + } + if (size >= INT_MAX / sizeof(int)) { + cc_.reportError(expr->pos(), Message_ArraySizeTooBig); + return; + } + + if (check) { + if (check->elements() == -1) { + cc_.reportError(expr->pos(), Message_ArraySizeCannotBeDetermined); + return; + } + + if (check->isCharArray()) { + if (check->elements() > size) { + cc_.reportError(node->pos(), Message_ArrayAssignmentTooBig); + return; + } + } else if (check->elements() != size) { + cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, unsigned(i)); + return; + } + } + } else { + if (init) { + cc_.reportError(node->pos(), Message_DynamicArrayHasInitializer); + return; + } + + // here until we get dynamic array support. + cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger); return; } } else { - assert(false); + // Infer the size from the initializer. + if (!init) { + cc_.reportError(node->pos(), Message_ArraySizeNotSpecified); + return; + } + if (init->elements() == -1) { + cc_.reportError(node->pos(), Message_ArraySizeCannotBeDetermined); + return; + } + size = init->elements(); } type = ZONE()->types()->unsafeArray(type, size); - - if (check) { - assert(check->elements() != -1); - assert(size != -1); - - // If we're assigning a string, we're allowed to pad with - // zeroes. - if (check->isCharArray()) { - assert(type->isCharArray()); - assert(type->elements() >= check->elements()); - } else { - assert(false); - } - } + if (!type) + return; if (check) check = check->contained(); @@ -414,10 +491,13 @@ } else { if (node->initialization()) { Type *right = visitForLoad(node->initialization()); - assert(TypeCheck(type, right)); + if (!right) + return; + if (!typeCheckAssignment(node, type, right, true)) + return; } if (type->primitive() == PrimitiveType_Char) { - assert(false); + cc_.reportError(node->pos(), Message_CannotDeclareStringCell); return; } } @@ -454,6 +534,11 @@ if (!type) return false; + if (type->primitive() == PrimitiveType_Char) { + cc_.reportError(node->pos(), Message_CannotDeclareStringCell); + return false; + } + if (parameter->reference()) { type = ZONE()->types()->getReference(type); if (!type) @@ -479,60 +564,137 @@ void SemanticAnalysis::visit(FunctionStatement *node) { + function_ = node->fun()->type(); scope_ = node->scope(); - - // :TODO: call this ahead of time. - if (!preanalyze(node)) - return; - node->body()->accept(this); scope_ = NULL; + function_ = NULL; } -// :TODO: use handles -static inline bool -TypeCheckParameter(Type *given, Type *actual) +bool +SemanticAnalysis::typeCheckArrayAssignment(AstNode *node, Type *given, Type *actual, bool forAssign) { - if (actual->isReference()) { - if (given->isReference()) - given = given->contained(); - return TypeCheck(given, actual->contained()); + if (!given->isArray()) { + cc_.reportError(node->pos(), Message_ExpectedArray); + return false; + } + if (given->levels() != actual->levels()) { + cc_.reportError(node->pos(), Message_ArrayDimensionsMismatch); + return false; + } + + unsigned level = 0; + while (given->isArray()) { + if (actual->elements() != -1) { + if (forAssign) { + if (given->elements() == -1 || actual->elements() == -1) { + cc_.reportError(node->pos(), Message_ArraySizeNotKnown); + return false; + } + + if (actual->isCharArray()) { + // String assignment can have a truncated |given|. + if (given->elements() > actual->elements()) { + cc_.reportError(node->pos(), Message_ArrayAssignmentTooBig); + return false; + } + } else { + if (given->elements() != actual->elements()) { + cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, level); + return false; + } + } + } else { + if (actual->elements() != -1 && given->elements() != actual->elements()) { + cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, level); + return false; + } + } + } + given = given->contained(); + actual = actual->contained(); } - return TypeCheck(given, actual); + if (given->primitive() != actual->primitive()) { + const char *givenTag = given->tag() ? given->tag() : "_"; + const char *actualTag = given->tag() ? given->tag() : "_"; + cc_.reportError(node->pos(), Message_TagMismatch, actualTag, givenTag); + return false; + } + + return true; +} + +bool +SemanticAnalysis::typeCheckAssignment(AstNode *node, Type *given, Type *actual, bool forAssign) +{ + if (actual->isArray()) + return typeCheckArrayAssignment(node, given, actual, forAssign); + + if (given->isArray()) { + cc_.reportError(node->pos(), Message_CannotAssignArrayToScalar); + return false; + } + + if (!given->isPrimitive() || !actual->primitive()) { + cc_.reportError(node->pos(), Message_UnsupportedType); + return false; + } + + // The given type may not have been dereferenced. + if (given->isReference()) + given = given->contained(); + if (actual->isReference()) + actual = given->contained(); + + if (given->primitive() != actual->primitive()) { + const char *givenTag = given->tag() ? given->tag() : "_"; + const char *actualTag = given->tag() ? given->tag() : "_"; + cc_.reportError(node->pos(), Message_TagMismatch, actualTag, givenTag); + return false; + } + + return true; +} + +bool +SemanticAnalysis::typeCheckParameter(AstNode *node, Type *given, Type *actual) +{ + return typeCheckAssignment(node, given, actual, false); } void SemanticAnalysis::visit(CallExpression *node) { - visitForLoad(node->callee()); - - // :TODO: use locals - Type *type = node->callee()->type(); + Type *type = visitForLoad(node->callee()); if (!type) return; - assert(type->isFunction()); - assert(node->arguments()->size() == type->parameters()->length()); + if (!type->isFunction()) { + cc_.reportError(node->pos(), Message_CalleeNotFunction); + return; + } + if (node->arguments()->size() != type->parameters()->length()) { + cc_.reportError(node->pos(), Message_ArgumentCountMismatch); + return; + } for (unsigned i = 0; i < node->arguments()->size(); i++) { // Note: we don't compute references unless the callee needs a // reference type. Expression *expression = node->arguments()->at(i); - expression->accept(this); - - Type *given = expression->type(); + Type *given = visitAndApplyType(expression); if (!given) return; // If it's an IndexExpression, we special case the ability to compute - // a reference. Maybe we should compute actual Ref types for - // IndexExpression? + // a reference. if (expression->isIndexExpression()) given = given->contained(); - Type *actual = Type::cast(type->parameters()->at(i)); - assert(TypeCheckParameter(given, actual)); + Type *actual = type->parameterAt(i); + if (!typeCheckParameter(expression, given, actual)) + return; // 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. @@ -544,7 +706,13 @@ void SemanticAnalysis::visit(IfStatement *node) { - visitForLoad(node->condition()); + Type *type = visitForLoad(node->condition()); + if (!type) + return; + + if (!canTest(node, type)) + return; + node->ifTrue()->accept(this); if (node->ifFalse()) node->ifFalse()->accept(this); @@ -557,12 +725,15 @@ Type *left = visitForRef(node->left()); Type *right = visitForLoad(node->right()); - if (!right || !right->isInt32()) { - typeError(node->right(), "integer"); + if (!left || !right) + return; + + if (!right->isInt32()) { + cc_.reportError(node->right()->pos(), Message_IndexMustBeInteger); return; } - if (!left || !left->isArray() || !left->contained()->isPrimitive()) { - typeError(node->left(), "array"); + if (!left->isArray()) { + cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray); return; } node->setType(left); @@ -571,8 +742,16 @@ void SemanticAnalysis::visit(EnumStatement *node) { - // :TODO: test "enum Float { ..." + // Forbid the blessed types. Name *tag = node->name(); + if (!strcmp(tag, "Float") || + !strcmp(tag, "String") || + !strcmp(tag, "any") || + !strcmp(tag, "bool")) + { + cc_.reportError(node->pos(), Message_InvalidEnumName); + return; + } Type *type = ZONE()->types()->getPrimitiveForTag(tag); if (!type) @@ -585,8 +764,10 @@ BoxedPrimitive box = BoxedPrimitive::Int(value); if (entry.expr) { entry.expr->accept(this); - if (!EvaluateForConstant(entry.expr, &box)) - fprintf(stderr, "expected constant integer expression\n"); + if (!EvaluateForConstant(entry.expr, &box)) { + cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant); + return; + } assert(box.type() == PrimitiveType_Int32); value = box.toInt(); @@ -624,11 +805,13 @@ void SemanticAnalysis::visit(BreakStatement *node) { - assert(loopDepth_ > 0); + if (loopDepth_ == 0) + cc_.reportError(node->pos(), Message_BreakOutsideLoop); } void SemanticAnalysis::visit(ContinueStatement *node) { - assert(loopDepth_ > 0); + if (loopDepth_ == 0) + cc_.reportError(node->pos(), Message_ContinueOutsideLoop); }