Implemented const for references and arrays.
1 /* vim: set ts=4 sw=4 tw=99 et:
3 * Copyright (C) 2012 David Anderson
5 * This file is part of JITCraft.
7 * JITCraft is free software: you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation, either version 3 of the License, or (at your option)
12 * Foobar is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with
17 * JITCraft. If not, see http://www.gnu.org/licenses/.
19 #include "SemanticAnalysis.h"
22 #include "TypeManager.h"
30 IsPrimitiveType(Type *type, PrimitiveType primitive)
35 if (!type->isPrimitive())
38 return primitive == type->primitive();
42 EvaluateBinaryOperation(TokenKind kind, const BoxedPrimitive &left, const BoxedPrimitive &right,
47 *out = BoxedPrimitive::Int(left.toInt() + right.toInt());
51 *out = BoxedPrimitive::Int(left.toInt() << right.toInt());
60 EvaluateForConstant(Expression *expression, BoxedPrimitive *out)
62 switch (expression->kind()) {
63 case AstNode::kBinaryExpression:
67 BinaryExpression *b = expression->toBinaryExpression();
68 if (!EvaluateForConstant(b->left(), &left) ||
69 !EvaluateForConstant(b->right(), &right))
73 return EvaluateBinaryOperation(b->token(), left, right, out);
76 case AstNode::kNameProxy:
78 NameProxy *proxy = expression->toNameProxy();
79 if (proxy->binding()->kind() != BoundName::CONSTANT)
82 NamedConstant *c = proxy->binding()->toNamedConstant();
83 if (!IsPrimitiveType(c->type(), PrimitiveType_Int32))
90 case AstNode::kIntegerLiteral:
91 *out = BoxedPrimitive::Int(expression->toIntegerLiteral()->value());
99 SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, ParseTree *tree)
109 SemanticAnalysis::analyze()
114 for (size_t i = 0; i < tree_->statements()->length(); i++) {
115 Statement *statement = tree_->statements()->at(i);
116 if (statement->isFunctionStatement()) {
117 FunctionStatement *funstmt = statement->toFunctionStatement();
118 if (!preanalyze(funstmt))
123 for (size_t i = 0; i < tree_->statements()->length(); i++) {
124 Statement *statement = tree_->statements()->at(i);
125 statement->accept(this);
128 return !cc_.failures();
132 SemanticAnalysis::visitAndApplyType(Expression *expression)
134 expression->accept(this);
135 return expression->type();
139 SemanticAnalysis::visitForLoad(Expression *expression)
141 Local<Type> type(zone_, visitAndApplyType(expression));
145 if (type->isReference()) {
146 expression->setType(type->contained());
147 } else if (expression->isIndexExpression()) {
149 expression->setType(type->contained());
152 return expression->type();
156 SemanticAnalysis::visitForRef(Expression *expression)
158 expression->accept(this);
159 return expression->type();
162 // :TODO: this leaks an interior pointer.
163 static inline const char *
164 GetTypeName(Type *type)
168 if (type->isFunction())
170 if (type->isReference())
171 type = type->contained();
173 return type->tag()->chars();
178 SemanticAnalysis::unaryTypeError(AstNode *node, TokenKind token, Handle<Type> type)
180 const char *op = TokenNames[token];
181 const char *name = GetTypeName(type);
183 cc_.reportError(node->pos(), Message_UnaryTypeError, op, name);
187 SemanticAnalysis::binaryTypeError(AstNode *node, TokenKind token, Handle<Type> left, Handle<Type> right)
189 const char *op = TokenNames[token];
190 const char *leftName = GetTypeName(left);
191 const char *rightName = GetTypeName(right);
193 cc_.reportError(node->pos(), Message_BinaryTypeError, op, leftName, rightName);
197 SemanticAnalysis::typeCheckBinary(AstNode *node, TokenKind token, Handle<Type> left, Handle<Type> right)
199 if ((token >= TOK_PLUS && token <= TOK_SLASH) ||
200 (token >= TOK_ASSIGN_ADD && token <= TOK_ASSIGN_DIV))
202 // If either side is a float, the other side must be a float or int.
203 if (left->isFloat() || right->isFloat()) {
204 Type *floatType = left->isFloat() ? left : right;
205 Type *other = left->isFloat() ? right : left;
206 if (other->isFloat() || (other->isInt32() && !other->tag()))
211 if ((!left->isInt32() || left->tag()) ||
212 (!right->isInt32() || right->tag()))
214 binaryTypeError(node, token, left, right);
222 SemanticAnalysis::canTest(AstNode *node, Handle<Type> type)
224 if (type->isArray()) {
225 cc_.reportError(node->pos(), Message_ArrayIsNotBoolean);
232 SemanticAnalysis::visitForLValue(Expression *expression)
234 if (expression->isIndexExpression()) {
235 Type *type = visitAndApplyType(expression);
239 if (type->isConst()) {
240 cc_.reportError(expression->pos(), Message_ReferenceIsConstant);
244 return type->contained();
247 if (!expression->isNameProxy()) {
248 cc_.reportError(expression->pos(), Message_ExpectedLValue);
252 NameProxy *proxy = expression->toNameProxy();
253 if (!proxy->binding()->kind() == BoundName::VARIABLE) {
254 cc_.reportError(expression->pos(), Message_ExpectedLValue);
258 Variable *var = proxy->binding()->toVariable();
262 if (var->type()->isReference()) {
263 if (var->type()->isConst()) {
264 cc_.reportError(expression->pos(), Message_ReferenceIsConstant);
267 return var->type()->contained();
273 SemanticAnalysis::visit(NameProxy *name)
275 name->setType(name->binding()->type());
279 SemanticAnalysis::visit(Assignment *node)
281 Local<Type> exprType(zone_, visitForLoad(node->expression()));
285 Local<Type> type(zone_, visitForLValue(node->lvalue()));
289 if (node->token() != TOK_ASSIGN) {
290 if (!typeCheckBinary(node, node->token(), type, exprType))
294 if (!typeCheckAssignment(node, exprType, type, true))
301 SemanticAnalysis::visit(IncDecExpression *node)
303 Local<Type> type(zone_, visitForLValue(node->expression()));
307 assert(!type->isReference());
309 if ((!type->isInt32() || (type->isInt32() && type->tag())) && !type->isFloat()) {
310 unaryTypeError(node, node->token(), type);
318 SemanticAnalysis::visit(BinaryExpression *node)
320 Local<Type> left(zone_, visitForLoad(node->left()));
321 Local<Type> right(zone_, visitForLoad(node->right()));
325 Local<Type> result(zone_, typeCheckBinary(node, node->token(), left, right));
329 node->setType(result);
333 SemanticAnalysis::visit(ReturnStatement *node)
335 Local<Type> type(zone_, visitForLoad(node->expression()));
339 Local<Type> returnType(zone_, function_->returnType());
340 if (!typeCheckAssignment(node, type, returnType, true))
345 SemanticAnalysis::visit(ForStatement *node)
349 scope_ = node->scope();
351 if (node->initialization())
352 node->initialization()->accept(this);
354 if (node->condition()) {
355 Local<Type> type(zone_, visitForLoad(node->condition()));
356 if (!type || !canTest(node->condition(), type)) {
363 node->update()->accept(this);
365 node->body()->accept(this);
368 scope_ = scope_->enclosing();
373 SemanticAnalysis::visit(WhileStatement *node)
377 Local<Type> type(zone_, visitForLoad(node->condition()));
378 if (!type || !canTest(node->condition(), type)) {
383 node->body()->accept(this);
388 SemanticAnalysis::visit(BlockStatement *node)
391 scope_ = node->scope();
392 for (size_t i = 0; i < node->statements()->length(); i++) {
393 Statement *statement = node->statements()->at(i);
394 statement->accept(this);
397 scope_ = scope_->enclosing();
401 SemanticAnalysis::visit(VariableDeclaration *node)
403 Local<String> tag(zone_, node->tag());
404 Local<Type> type(zone_, zone_->types()->getPrimitiveForTag(tag));
408 if (node->postDimensions()) {
409 Local<Type> init(zone_);
410 if (node->initialization()) {
411 if ((init = visitAndApplyType(node->initialization())) == NULL)
415 if (node->postDimensions()->length() > Type::MAX_UNSAFE_ARRAY_DEPTH) {
416 cc_.reportError(node->pos(), Message_MaximumArrayDepth);
420 // If we have an initializer, make sure it's the same type.
421 if (init && node->postDimensions()->length() != init->levels()) {
422 cc_.reportError(node->pos(), Message_ArrayInitializerDepth);
426 Local<Type> check(zone_, init);
427 for (size_t i = node->postDimensions()->length() - 1; i < node->postDimensions()->length(); i--) {
428 Expression *expr = node->postDimensions()->at(i);
432 Local<Type> exprType(zone_, visitForLoad(expr));
436 if (!IsPrimitiveType(exprType, PrimitiveType_Int32)) {
437 cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger);
442 if (EvaluateForConstant(expr, &box) && box.type() == PrimitiveType_Int32) {
445 cc_.reportError(expr->pos(), Message_BadArraySize);
448 if (size >= INT_MAX / sizeof(int)) {
449 cc_.reportError(expr->pos(), Message_ArraySizeTooBig);
454 if (check->elements() == -1) {
455 cc_.reportError(expr->pos(), Message_ArraySizeCannotBeDetermined);
459 if (check->isCharArray()) {
460 if (check->elements() > size) {
461 cc_.reportError(node->pos(), Message_ArrayAssignmentTooBig);
464 } else if (check->elements() != size) {
465 cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, unsigned(i));
471 cc_.reportError(node->pos(), Message_DynamicArrayHasInitializer);
475 // here until we get dynamic array support.
476 cc_.reportError(expr->pos(), Message_ArrayDimensionNotInteger);
480 // Infer the size from the initializer.
482 cc_.reportError(node->pos(), Message_ArraySizeNotSpecified);
485 if (init->elements() == -1) {
486 cc_.reportError(node->pos(), Message_ArraySizeCannotBeDetermined);
489 size = init->elements();
492 type = zone_->types()->unsafeArray(type, size, false);
497 check = check->contained();
500 // Right now, any array is allowed on the LIFO heap. In case the
501 // runtime actually decides to use the LIFO heap, we must reserve
502 // a slot so we can unwind the heap when the current scope leaves.
503 scope_->setUsesLifoHeap();
505 if (node->initialization()) {
506 Local<Type> right(zone_, visitForLoad(node->initialization()));
509 if (!typeCheckAssignment(node, type, right, true))
512 if (type->primitive() == PrimitiveType_Char) {
513 cc_.reportError(node->pos(), Message_CannotDeclareStringCell);
518 node->variable()->setType(type);
522 SemanticAnalysis::visit(IntegerLiteral *node)
524 node->setType(zone_->types()->getPrimitive(PrimitiveType_Int32));
528 SemanticAnalysis::visit(ExpressionStatement *node)
530 node->expression()->accept(this);
534 SemanticAnalysis::preanalyze(FunctionStatement *node)
536 Local<FixedArray> args(zone_, FixedArray::New(zone_, node->parameters()->length(), Heap::Tenure_Old));
540 // Apply types to arguments.
541 for (size_t i = 0; i < node->parameters()->length(); i++) {
542 Parameter *parameter = node->parameters()->at(i);
544 Local<String> tag(zone_, parameter->tag());
545 Local<Type> type(zone_, zone_->types()->getPrimitiveForTag(tag));
549 if (type->primitive() == PrimitiveType_Char) {
550 cc_.reportError(node->pos(), Message_CannotDeclareStringCell);
554 if (parameter->reference()) {
555 if (parameter->dimensions()) {
556 cc_.reportError(parameter->variable()->pos(), Message_ArrayCantBeReference);
560 type = zone_->types()->getReference(type, parameter->isConst());
563 } else if (parameter->dimensions()) {
564 if (parameter->dimensions()->length() > Type::MAX_UNSAFE_ARRAY_DEPTH) {
565 cc_.reportError(node->pos(), Message_MaximumArrayDepth);
569 for (size_t i = parameter->dimensions()->length() - 1;
570 i < parameter->dimensions()->length();
573 int size = Type::UNKNOWN_SIZE;
574 if (Expression *expr = parameter->dimensions()->at(i)) {
576 if (!EvaluateForConstant(expr, &box)) {
577 cc_.reportError(expr->pos(), Message_ArraySizeMustBeConstant);
583 type = zone_->types()->unsafeArray(type, size, parameter->isConst());
589 args->set(zone_, i, type);
590 parameter->variable()->setType(type);
593 Local<Type> returnType(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32));
597 Local<Type> funtype(zone_, zone_->types()->newFunction(returnType, args));
601 // If the function already has a type, it was forward declared before, and
602 // now this is our chance to type check the second declaration.
603 if (node->fun()->type()) {
604 Local<Type> forward(zone_, node->fun()->type());
605 if (!Type::Compare(funtype, forward)) {
606 cc_.reportError(node->pos(), Message_ForwardDeclareMismatches);
609 node->fun()->setForwardType(forward);
612 node->fun()->setType(funtype);
618 SemanticAnalysis::visit(FunctionStatement *node)
620 function_ = node->fun()->type();
621 scope_ = node->scope();
623 node->body()->accept(this);
629 SemanticAnalysis::typeCheckArrayAssignment(AstNode *node, Handle<Type> argGiven, Handle<Type> argActual, bool forAssign)
631 Local<Type> actual(zone_, argActual);
632 Local<Type> given(zone_, argGiven);
634 if (!given->isArray()) {
635 cc_.reportError(node->pos(), Message_ExpectedArray);
638 if (given->levels() != actual->levels()) {
639 cc_.reportError(node->pos(), Message_ArrayDimensionsMismatch);
644 while (given->isArray()) {
645 if (actual->elements() != -1) {
647 if (given->elements() == -1 || actual->elements() == -1) {
648 cc_.reportError(node->pos(), Message_ArraySizeNotKnown);
652 if (actual->isCharArray()) {
653 // String assignment can have a truncated |given|.
654 if (given->elements() > actual->elements()) {
655 cc_.reportError(node->pos(), Message_ArrayAssignmentTooBig);
659 if (given->elements() != actual->elements()) {
660 cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, level);
665 if (actual->elements() != -1 && given->elements() != actual->elements()) {
666 cc_.reportError(node->pos(), Message_ArrayDimensionSizeMismatch, level);
671 given = given->contained();
672 actual = actual->contained();
675 if (given->primitive() != actual->primitive()) {
676 // :TODO: leaks an interior pointer.
677 const char *givenTag = given->tag() ? given->tag()->chars() : "_";
678 const char *actualTag = given->tag() ? given->tag()->chars() : "_";
679 cc_.reportError(node->pos(), Message_TagMismatch, actualTag, givenTag);
687 SemanticAnalysis::typeCheckAssignment(AstNode *node, Handle<Type> argGiven, Handle<Type> argActual, bool forAssign)
689 Local<Type> actual(zone_, argActual);
690 Local<Type> given(zone_, argGiven);
692 if (actual->isArray())
693 return typeCheckArrayAssignment(node, given, actual, forAssign);
695 if (given->isArray()) {
696 cc_.reportError(node->pos(), Message_CannotAssignArrayToScalar);
700 // The given type may not have been dereferenced.
701 if (given->isReference())
702 given = given->contained();
703 if (actual->isReference())
704 actual = actual->contained();
706 if (!given->isPrimitive() || !actual->isPrimitive()) {
707 cc_.reportError(node->pos(), Message_UnsupportedType);
711 if (given->primitive() != actual->primitive()) {
712 // :TODO: leaks an interior pointer
713 const char *givenTag = given->tag() ? given->tag()->chars() : "_";
714 const char *actualTag = given->tag() ? given->tag()->chars() : "_";
715 cc_.reportError(node->pos(), Message_TagMismatch, actualTag, givenTag);
723 SemanticAnalysis::typeCheckParameter(AstNode *node, Handle<Type> given, Handle<Type> actual)
725 return typeCheckAssignment(node, given, actual, false);
729 SemanticAnalysis::visit(CallExpression *node)
731 Local<Type> type(zone_, visitForLoad(node->callee()));
735 if (!type->isFunction()) {
736 cc_.reportError(node->pos(), Message_CalleeNotFunction);
739 if (node->arguments()->length() != type->parameters()->length()) {
740 cc_.reportError(node->pos(), Message_ArgumentCountMismatch);
744 // Currently, only calls from a NameProxy are possible.
745 NamedFunction *fun = node->callee()->toNameProxy()->binding()->toNamedFunction();
746 if (fun->isForward()) {
747 cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars());
751 for (unsigned i = 0; i < node->arguments()->length(); i++) {
752 // Note: we don't compute references unless the callee needs a
754 Expression *expression = node->arguments()->at(i);
755 Local<Type> given(zone_, visitAndApplyType(expression));
759 // If it's an IndexExpression, we special case the ability to compute
761 if (expression->isIndexExpression())
762 given = given->contained();
764 Local<Type> actual(zone_, type->parameterAt(i));
765 if (!typeCheckParameter(expression, given, actual))
768 // If the actual is a reference, and the given is not, we'll need to
769 // compute a reference, which may be allocated on the stack.
770 if (actual->isReference() && !given->isReference())
771 scope_->setUsesLifoHeap();
774 node->setType(fun->type()->returnType());
778 SemanticAnalysis::visit(IfStatement *node)
780 Local<Type> type(zone_, visitForLoad(node->condition()));
784 if (!canTest(node, type))
787 node->ifTrue()->accept(this);
789 node->ifFalse()->accept(this);
793 SemanticAnalysis::visit(IndexExpression *node)
795 Local<Type> left(zone_, visitForRef(node->left()));
796 Local<Type> right(zone_, visitForLoad(node->right()));
801 if (!right->isInt32()) {
802 cc_.reportError(node->right()->pos(), Message_IndexMustBeInteger);
805 if (!left->isArray()) {
806 cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray);
813 SemanticAnalysis::visit(EnumStatement *node)
815 // Forbid the blessed types.
816 Local<String> tag(zone_, node->name());
817 if (!strcmp(tag->chars(), "Float") ||
818 !strcmp(tag->chars(), "String") ||
819 !strcmp(tag->chars(), "any") ||
820 !strcmp(tag->chars(), "bool"))
822 cc_.reportError(node->pos(), Message_InvalidEnumName);
826 Local<Type> type(zone_, zone_->types()->getPrimitiveForTag(tag));
831 for (size_t i = 0; i < node->entries()->length(); i++) {
832 EnumStatement::Entry &entry = node->entries()->at(i);
834 BoxedPrimitive box = BoxedPrimitive::Int(value);
836 entry.expr->accept(this);
837 if (!EvaluateForConstant(entry.expr, &box)) {
838 cc_.reportError(entry.expr->pos(), Message_EnumFieldNotConstant);
842 assert(box.type() == PrimitiveType_Int32);
846 entry.name->set(type, box);
852 SemanticAnalysis::visit(StringLiteral *node)
854 Local<Type> type(zone_, zone_->types()->getPrimitive(PrimitiveType_Char));
858 type = Type::NewArray(zone_, type, node->string()->length() + 1, true);
866 SemanticAnalysis::visit(FloatLiteral *node)
868 Local<Type> type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float));
876 SemanticAnalysis::visit(BreakStatement *node)
879 cc_.reportError(node->pos(), Message_BreakOutsideLoop);
883 SemanticAnalysis::visit(ContinueStatement *node)
886 cc_.reportError(node->pos(), Message_ContinueOutsideLoop);