dvander@30: /* vim: set ts=4 sw=4 tw=99 et: dvander@30: * dvander@0: * Copyright (C) 2012 David Anderson dvander@0: * dvander@195: * This file is part of SourcePawn. dvander@0: * dvander@195: * SourcePawn is free software: you can redistribute it and/or modify it under dvander@0: * the terms of the GNU General Public License as published by the Free dvander@0: * Software Foundation, either version 3 of the License, or (at your option) dvander@0: * any later version. dvander@0: * dvander@195: * SourcePawn is distributed in the hope that it will be useful, but WITHOUT ANY dvander@0: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS dvander@0: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. dvander@0: * dvander@0: * You should have received a copy of the GNU General Public License along with dvander@195: * SourcePawn. If not, see http://www.gnu.org/licenses/. dvander@0: */ dvander@195: #include dvander@195: #include "../Zone.h" dvander@195: #include "../Strings.h" dvander@195: #include "../NativeTable.h" dvander@195: #include "../TypeManager.h" dvander@195: #include "../Modules.h" dvander@195: #include "../Code.h" dvander@0: #include "SemanticAnalysis.h" dvander@196: #include "../Heap-inl.h" dvander@0: dvander@59: using namespace ke; dvander@0: dvander@195: #define __ emitter_. dvander@2: dvander@195: typedef BytecodeEmitter::Label Label; dvander@2: dvander@195: class AutoEnterScope dvander@2: { dvander@195: public: dvander@195: AutoEnterScope(Scope **prevp, Scope *scope) dvander@195: : prevp_(prevp), dvander@195: prev_(*prevp), dvander@195: scope_(scope) dvander@195: { dvander@195: if (!scope_) dvander@195: return; dvander@195: prev_ = *prevp_; dvander@195: *prevp_ = scope; dvander@195: assert(prev_ == scope->enclosing()); dvander@195: } dvander@195: ~AutoEnterScope() dvander@195: { dvander@195: if (scope_) dvander@195: *prevp_ = prev_; dvander@195: } dvander@195: private: dvander@195: Scope **prevp_; dvander@195: Scope *prev_; dvander@195: Scope *scope_; dvander@195: }; dvander@144: dvander@195: SemanticAnalysis::SemanticAnalysis(Zone *zone, CompileContext &cc, TranslationUnit *unit) dvander@30: : zone_(zone), dvander@195: pool_(zone->pool()), dvander@30: cc_(cc), dvander@195: unit_(unit), dvander@195: hir_(NULL), dvander@195: emitter_(pool_), dvander@71: loop_(NULL) dvander@0: { dvander@0: } dvander@0: dvander@195: SemanticAnalysis::SemanticAnalysis(SemanticAnalysis *parent, Handle fun) dvander@195: : zone_(parent->zone_), dvander@195: pool_(parent->pool_), dvander@195: cc_(parent->cc_), dvander@195: unit_(parent->unit_), dvander@195: hir_(NULL), dvander@195: scope_(parent->scope_), dvander@195: emitter_(pool_), dvander@195: fun_(fun), dvander@232: loop_(NULL), dvander@232: control_(NULL) dvander@0: { dvander@137: dvander@4: } dvander@4: dvander@25: void dvander@195: SemanticAnalysis::analyze() dvander@21: { dvander@195: scope_ = unit_->globalScope(); dvander@195: dvander@195: for (size_t i = 0; i < unit_->tree()->statements()->length(); i++) { dvander@195: Statement *stmt = unit_->tree()->statements()->at(i); dvander@195: stmt->accept(this); dvander@195: } dvander@21: dvander@195: Local code(zone_, __ finish()); dvander@195: if (!code) dvander@195: return; dvander@251: code->setModule(unit_->module()); dvander@251: unit_->module()->setCode(code); dvander@195: dvander@195: #ifndef NDEBUG dvander@195: // Disassemble(code, stdout); dvander@195: #endif dvander@21: } dvander@21: dvander@25: void dvander@195: SemanticAnalysis::visitFunctionStatement(FunctionStatement *stmt) dvander@25: { dvander@195: FunctionSymbol *sym = stmt->sym(); dvander@195: Local type(zone_, sym->type()); dvander@22: dvander@195: assert(scope_->kind() == Scope::GLOBAL); dvander@79: dvander@195: if (stmt->token() == TOK_NATIVE) { dvander@195: // Define the native now. dvander@195: size_t index; dvander@195: if (!zone_->nativeTable()->add(stmt->name(), &index)) dvander@195: return; dvander@22: dvander@195: Local native(zone_, Native::New(zone_, type, index)); dvander@195: if (!native) dvander@195: return; dvander@25: dvander@195: __ object(native); dvander@195: __ setglobal(sym); dvander@195: __ pop(); dvander@23: } dvander@23: dvander@195: // If the function is a forward or native, we don't have to do anything. dvander@195: if (stmt->token() == TOK_NATIVE || stmt->token() == TOK_FORWARD) dvander@23: return; dvander@4: dvander@195: SemanticAnalysis child(this, type); dvander@195: Local funobj(zone_, child.compile(stmt)); dvander@195: if (!funobj) dvander@25: return; dvander@25: dvander@195: __ object(funobj); dvander@195: __ setglobal(sym); dvander@215: if (stmt->token() == TOK_PUBLIC) dvander@215: __ public_(); dvander@215: else dvander@215: __ pop(); dvander@0: } dvander@0: dvander@195: Function * dvander@195: SemanticAnalysis::compile(FunctionStatement *stmt) dvander@83: { dvander@195: __ setFirstLine(stmt->pos().line); dvander@83: dvander@195: AutoEnterScope argScope(&scope_, stmt->argScope()); dvander@220: dvander@220: for (size_t i = 0; i < stmt->signature().parameters()->length(); i++) { dvander@220: Parameter *param = stmt->signature().parameters()->at(i); dvander@220: param->sym()->allocate(VariableSymbol::Arg, i); dvander@220: } dvander@220: dvander@195: AutoEnterScope varScope(&scope_, stmt->varScope()); dvander@232: ControlStatus status = visitForControl(stmt->body()); dvander@4: dvander@232: if (!fun_->returnType()->isVoid()) { dvander@232: // If the function is not void, report if not all paths return a value. dvander@232: // All other incorrect return cases have been caught already. dvander@232: if (status.returns() == Returns_Sometimes) { dvander@232: cc_.reportError(stmt->pos(), Message_NotAllPathsReturnValue); dvander@232: return NULL; dvander@232: } dvander@232: dvander@232: if (status.returns() == Returns_Never) { dvander@232: cc_.reportError(stmt->pos(), Message_MissingReturnValue); dvander@232: return NULL; dvander@232: } dvander@232: } dvander@71: dvander@195: Local code(zone_, __ finish()); dvander@195: if (!code) dvander@195: return NULL; dvander@251: code->setModule(unit_->module()); dvander@25: dvander@195: Local fun(zone_, Function::New(zone_, code, fun_, stmt->name(), stmt->pos())); dvander@195: if (!fun) dvander@195: return NULL; dvander@25: dvander@195: #ifndef NDEBUG dvander@195: // Disassemble(code, stdout); dvander@195: #endif dvander@71: dvander@195: return fun; dvander@0: } dvander@0: dvander@9: void dvander@195: SemanticAnalysis::visitBlockStatement(BlockStatement *node) dvander@9: { dvander@195: AutoEnterScope scope(&scope_, node->scope()); dvander@71: dvander@195: for (size_t i = 0; i < node->statements()->length(); i++) { dvander@195: Statement *stmt = node->statements()->at(i); dvander@221: __ note_position(stmt->pos()); dvander@195: stmt->accept(this); dvander@195: assert(__ stackDepth() == 0); dvander@71: } dvander@9: } dvander@9: dvander@0: void dvander@233: SemanticAnalysis::visitEnumStatement(EnumStatement *node) dvander@233: { dvander@233: } dvander@233: dvander@233: void dvander@195: SemanticAnalysis::visitExpressionStatement(ExpressionStatement *node) dvander@145: { dvander@195: // While building HIR, we enter new pool and root scopes, since HIR dvander@195: // creation is purely temporary. dvander@195: PoolScope poolScope(zone_->pool()); dvander@195: RootScope rootScope(zone_); dvander@190: dvander@195: HIR *hir = eval(node->expression()); dvander@195: if (!hir) dvander@195: return; dvander@187: dvander@195: discharge(hir); dvander@220: if (!hir->type()->isVoid()) dvander@220: __ pop(); dvander@187: } dvander@187: dvander@195: void dvander@232: SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right) dvander@232: { dvander@232: // If both sides terminate in a return, we consider this a return as well. dvander@232: if (left.returns() == Returns_Always && right.returns() == Returns_Always) { dvander@232: terminate(Returns_Always); dvander@232: return; dvander@232: } dvander@232: dvander@232: ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never) dvander@232: ? Returns_Never dvander@232: : Returns_Sometimes; dvander@232: dvander@232: if (left.terminates() && right.terminates()) dvander@232: terminate(status); dvander@232: else dvander@232: returns(status); dvander@232: } dvander@232: dvander@232: SemanticAnalysis::ControlStatus dvander@232: SemanticAnalysis::visitForControl(Statement *statement) dvander@232: { dvander@232: ControlScope scope(&control_); dvander@232: statement->accept(this); dvander@232: return scope.status(); dvander@232: } dvander@232: dvander@232: void dvander@208: SemanticAnalysis::visitIfStatement(IfStatement *node) dvander@208: { dvander@208: Label join; dvander@208: dvander@232: ControlStatus trueBranch; dvander@232: ControlStatus falseBranch(false, Returns_Never); dvander@232: dvander@208: if (!node->ifFalse()) { dvander@208: Label success; dvander@208: dvander@208: visitForTest(node->condition(), &success, &join, &success); dvander@208: __ bind(&success); dvander@232: trueBranch = visitForControl(node->ifTrue()); dvander@208: } else { dvander@208: Label success, next; dvander@208: dvander@208: visitForTest(node->condition(), &success, &next, &success); dvander@208: __ bind(&success); dvander@232: trueBranch = visitForControl(node->ifTrue()); dvander@208: __ jump(&join); dvander@208: __ bind(&next); dvander@232: falseBranch = visitForControl(node->ifFalse()); dvander@208: } dvander@208: dvander@208: __ bind(&join); dvander@232: dvander@232: control_->merge(trueBranch, falseBranch); dvander@208: } dvander@208: dvander@208: void dvander@230: SemanticAnalysis::requireLifoStack() dvander@230: { dvander@230: if (!loop_) dvander@230: return; dvander@230: dvander@230: loop_->setUsesLifoStack(); dvander@230: } dvander@230: dvander@232: SemanticAnalysis::ControlStatus dvander@230: SemanticAnalysis::visitLoopBody(Statement *stmt) dvander@230: { dvander@230: // We always mark off a loop body, so on each edge, we can balance the dvander@230: // LIFO stack. Right now, we don't have the ability to budget the LIFO dvander@230: // stack ahead of time. If we turn out to never need the LIFO stack, we dvander@230: // mark it as unnecessary. (The interpreter can't really make use of this, dvander@230: // but the JIT can). dvander@230: // dvander@230: // Note that balancing the LIFO stack isn't even necessary, so this dvander@230: // feature may be removed. All this mechanism does is mitigate the dvander@230: // possibility that a long-running loop will overflow the LIFO region dvander@230: // and therefore start requiring nursery allocations. dvander@230: int pos = __ enterloop(); dvander@232: ControlStatus status = visitForControl(stmt); dvander@230: __ leaveloop(); dvander@230: dvander@230: if (loop_->usesLifoStack()) dvander@230: __ setLoopUsesLifoStack(pos); dvander@232: dvander@232: return status; dvander@230: } dvander@230: dvander@230: void dvander@195: SemanticAnalysis::visitForStatement(ForStatement *node) dvander@187: { dvander@195: AutoEnterScope scope(&scope_, node->scope()); dvander@195: PoolScope poolScope(zone_->pool()); dvander@195: RootScope rootScope(zone_); dvander@195: dvander@195: Label join, update; dvander@195: LoopScope loop(scope_, &join, &update, &loop_); dvander@195: dvander@195: if (node->initialization()) dvander@195: node->initialization()->accept(this); dvander@187: dvander@195: Label header, test; dvander@195: if (node->condition()) dvander@195: __ jump(&test); dvander@195: dvander@195: __ bind(&header); dvander@232: ControlStatus status = visitLoopBody(node->body()); dvander@195: dvander@195: __ bind(&update); dvander@195: if (node->update()) dvander@195: node->update()->accept(this); dvander@195: dvander@195: if (node->condition()) { dvander@195: __ bind(&test); dvander@195: visitForTest(node->condition(), &header, &join, &join); dvander@187: } else { dvander@195: __ jump(&header); dvander@187: } dvander@187: dvander@195: __ bind(&join); dvander@232: dvander@232: // If there was a condition, we treat this like a while loop. Otherwise, dvander@232: // the join point is only reachable if the loop contains a break. The dvander@232: // termination status doesn't tell us anything yet. dvander@232: if (node->condition() || loop.breaks()) dvander@232: control_->merge(status, ControlStatus(false, Returns_Never)); dvander@232: else dvander@232: control_->terminate(status.returns()); dvander@187: dvander@195: assert(__ stackDepth() == 0); dvander@187: } dvander@187: dvander@0: void dvander@203: SemanticAnalysis::visitWhileStatement(WhileStatement *node) dvander@203: { dvander@203: PoolScope poolScope(zone_->pool()); dvander@203: RootScope rootScope(zone_); dvander@203: dvander@203: Label header, join; dvander@203: dvander@203: __ bind(&header); dvander@203: dvander@232: ControlStatus status; dvander@232: dvander@203: LoopScope loop(scope_, &join, &header, &loop_); dvander@203: if (node->token() == TOK_WHILE) { dvander@203: Label body; dvander@203: visitForTest(node->condition(), &body, &join, &body); dvander@232: status = visitLoopBody(node->body()); dvander@203: __ jump(&header); dvander@203: } else { dvander@232: status = visitLoopBody(node->body()); dvander@203: visitForTest(node->condition(), &header, &join, &join); dvander@203: } dvander@203: dvander@203: __ bind(&join); dvander@203: dvander@232: // Detect loops that will always have a fork in the contro-flow graph. dvander@232: // This will be a |while| loop, or a |do-while| loop whose body does dvander@232: // not terminate. For now, we treat these as an |if| regardless of the dvander@232: // direction the branch can take. That means the control-flow is always dvander@232: // conditional. dvander@232: if (node->token() == TOK_WHILE || status.terminates() || loop.breaks()) { dvander@232: control_->merge(status, ControlStatus(false, Returns_Never)); dvander@232: } else { dvander@232: // At this point, we've got a do-while loop with a |return| or dvander@232: // |continue|, meaning that the rest of the block is unreachable. dvander@232: control_->terminate(status.returns()); dvander@232: } dvander@232: dvander@203: assert(__ stackDepth() == 0); dvander@203: } dvander@203: dvander@212: static inline Heap::Tenure dvander@212: InferTenure(Symbol *sym) dvander@212: { dvander@212: if (sym->scope()->kind() == Scope::GLOBAL) dvander@212: return Heap::Tenure_Old; dvander@230: dvander@230: // SP2's array semantics allow any array kind to escape, so we must use dvander@230: // the normal heap. (Hopefully escape analysis can ease this pain later). dvander@230: if (sym->type()->isArray()) dvander@230: return Heap::Tenure_Default; dvander@230: dvander@212: return Heap::Tenure_Default; dvander@212: } dvander@212: dvander@203: void dvander@195: SemanticAnalysis::visitVariableDeclaration(VariableDeclaration *node) dvander@0: { dvander@195: PoolScope poolScope(zone_->pool()); dvander@195: RootScope rootScope(zone_); dvander@195: dvander@234: Local type(zone_, node->sym()->type()); dvander@234: dvander@237: if (node->sym()->type()->isVoid()) { dvander@237: cc_.reportError(node->type()->pos(), Message_CannotDeclareVoid); dvander@237: return; dvander@237: } dvander@237: dvander@195: if (node->initialization()) { dvander@212: HIR *hir = NULL; dvander@212: dvander@195: if ((hir = eval(node->initialization())) == NULL) dvander@195: return; dvander@236: if ((hir = coerce(hir, type, Coerce_Decl)) == NULL) dvander@195: return; dvander@212: dvander@212: discharge(hir); dvander@195: } else { dvander@234: if (type->isInt32() || type->isEnum() || dvander@234: type->isChar() || type->isBool()) dvander@212: { dvander@212: __ int_(0); dvander@234: } else if (type->isFloat()) { dvander@212: __ float_(0); dvander@234: } else if (type->isArray()) { dvander@234: Local atype(zone_, ArrayType::cast(type)); dvander@256: Local map(zone_, atype->newMap()); dvander@212: if (!map) dvander@212: return; dvander@212: dvander@234: if (atype->isFixedLength()) { dvander@212: __ newarray(OP_NEWFIXED, map, InferTenure(node->sym())); dvander@236: } else if (node->dims()->at(0)) { dvander@236: // This may not be true if we allow typedefing, so :TODO: dvander@236: // figure out proper semantics. dvander@236: assert(node->dims()->length() == atype->levels()); dvander@236: dvander@236: Local i32(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); dvander@236: for (size_t i = 0; i < node->dims()->length(); i++) { dvander@236: Expression *expr = node->dims()->at(i); dvander@236: if (expr) { dvander@236: HIR *hir = eval(expr); dvander@236: if (!hir || ((hir = coerce(hir, i32, Coerce_Arg)) == NULL)) dvander@236: return; dvander@236: discharge(hir); dvander@236: } else { dvander@236: __ int_(0); dvander@236: } dvander@236: } dvander@212: __ newarray(OP_NEWSIZED, map, InferTenure(node->sym())); dvander@212: } else { dvander@212: __ newarray(OP_NEWEMPTY, map, InferTenure(node->sym())); dvander@212: } dvander@234: } else { dvander@234: assert(false); dvander@212: } dvander@195: } dvander@195: dvander@234: if (node->sym()->scope()->kind() != Scope::GLOBAL) { dvander@234: if (!__ allocate(node->sym())) dvander@234: return; dvander@234: } dvander@0: dvander@195: set(node->sym()); dvander@195: __ pop(); dvander@195: } dvander@195: dvander@195: void dvander@195: SemanticAnalysis::visitReturnStatement(ReturnStatement *node) dvander@195: { dvander@232: control_->returns(Returns_Always); dvander@232: dvander@195: if (!node->expression()) { dvander@195: if (!fun_->returnType()->isVoid()) { dvander@232: cc_.reportError(node->pos(), Message_MissingReturnValue); dvander@0: return; dvander@0: } dvander@0: dvander@195: __ returnvoid(); dvander@195: return; dvander@0: } dvander@0: dvander@232: if (fun_->returnType()->isVoid()) { dvander@232: cc_.reportError(node->pos(), Message_UsedVoidReturn); dvander@232: return; dvander@232: } dvander@232: dvander@195: PoolScope poolScope(zone_->pool()); dvander@195: RootScope rootScope(zone_); dvander@195: dvander@195: HIR *hir = eval(node->expression()); dvander@195: if (!hir) dvander@195: return; dvander@147: dvander@195: Local returnType(zone_, fun_->returnType()); dvander@195: if ((hir = coerce(hir, returnType, Coerce_Return)) == NULL) dvander@195: return; dvander@195: dvander@195: discharge(hir); dvander@232: dvander@232: if (hir->type()->isVoid()) dvander@232: __ returnvoid(); dvander@232: else dvander@232: __ return_(); dvander@0: } dvander@0: dvander@0: void dvander@195: SemanticAnalysis::visitBinaryExpression(BinaryExpression *node) dvander@143: { dvander@195: HIR *left = eval(node->left()); dvander@195: HIR *right = eval(node->right()); dvander@195: if (!left || !right) dvander@195: return; dvander@168: dvander@221: hir_ = binary(node, node->token(), left, right); dvander@168: } dvander@168: dvander@14: bool dvander@195: SemanticAnalysis::checkArgumentCount(Handle type, unsigned actual) dvander@168: { dvander@168: if (type->parameters()->length() == actual) dvander@168: return true; dvander@168: dvander@168: if (actual > type->parameters()->length()) { dvander@168: if (!type->isNative() || !type->isNativeVariadic()) dvander@168: return false; dvander@168: return true; dvander@168: } dvander@168: dvander@168: if (!type->defaults()) dvander@168: return false; dvander@168: dvander@168: // Missing arguments. See if there's a default argument for each missing dvander@168: // actual. dvander@168: for (unsigned i = actual; i < type->parameters()->length(); i++) { dvander@168: if (!type->defaults()->at(i)) dvander@168: return false; dvander@168: } dvander@168: dvander@168: return true; dvander@14: } dvander@14: dvander@258: static unsigned dvander@258: BindImplicitName(Module *from, ImplicitSymbol *sym) dvander@258: { dvander@258: if (sym->bound()) dvander@258: return sym->fieldIndex(); dvander@258: dvander@258: Module *module = Module::cast(from->getImportAt(sym->index())); dvander@258: StructMap *map = module->globals()->map(); dvander@258: dvander@258: unsigned fieldIndex; dvander@258: Descriptor *desc = map->type()->lookupField(sym->name(), &fieldIndex); dvander@258: assert(desc && desc->type() == sym->type()); dvander@258: dvander@258: sym->bind(fieldIndex); dvander@258: return fieldIndex; dvander@258: } dvander@258: dvander@0: void dvander@195: SemanticAnalysis::visitNameProxy(NameProxy *proxy) dvander@0: { dvander@195: Symbol *sym = proxy->sym(); dvander@195: dvander@233: switch (sym->kind()) { dvander@256: case Symbol::kImport: dvander@256: { dvander@256: ImportSymbol *import = sym->toImport(); dvander@256: hir_ = new (pool_) HImport(proxy, sym->type(), import->index()); dvander@256: break; dvander@256: } dvander@256: dvander@258: case Symbol::kImplicit: dvander@258: { dvander@258: ImplicitSymbol *import = sym->toImplicit(); dvander@258: unsigned fieldIndex = BindImplicitName(unit_->module(), import); dvander@258: dvander@258: hir_ = new (pool_) HImport(proxy, zone_->types()->getImportable(), import->index()); dvander@258: hir_ = new (pool_) HField(proxy, hir_, sym->type(), fieldIndex); dvander@258: break; dvander@258: } dvander@258: dvander@233: case Symbol::kConstant: dvander@233: { dvander@233: ConstantSymbol *val = sym->toConstant(); dvander@233: hir_ = new (pool_) HInteger(proxy, sym->type(), val->value().toInt()); dvander@233: break; dvander@233: } dvander@233: dvander@233: // This is kind of screwy. Symbol::kFunction is like a constant we can't dvander@233: // evaluate yet. Maybe we should extract the GLOBAL case and say that dvander@233: // kFunction is only used for global functions (and use const vars for dvander@233: // local functions). dvander@233: case Symbol::kFunction: dvander@233: case Symbol::kVariable: dvander@233: { dvander@233: Scope *in = sym->scope(); dvander@233: switch (in->kind()) { dvander@233: case Scope::GLOBAL: dvander@233: hir_ = new (pool_) HGlobal(proxy, sym); dvander@233: break; dvander@14: dvander@233: case Scope::FUNCTION: dvander@233: { dvander@233: VariableSymbol *var = sym->toVariable(); dvander@233: assert(var->storage() == VariableSymbol::Arg); dvander@233: hir_ = new (pool_) HLocal(proxy, var); dvander@233: break; dvander@233: } dvander@233: dvander@233: default: dvander@233: { dvander@233: VariableSymbol *var = sym->toVariable(); dvander@233: assert(in->kind() == Scope::BLOCK); dvander@233: assert(var->storage() == VariableSymbol::Local); dvander@233: hir_ = new (pool_) HLocal(proxy, var); dvander@233: break; dvander@233: } dvander@233: } dvander@233: break; dvander@233: } dvander@220: dvander@195: default: dvander@233: // :TODO: dvander@233: assert(false); dvander@195: } dvander@195: } dvander@195: dvander@195: void dvander@245: SemanticAnalysis::visitBooleanLiteral(BooleanLiteral *node) dvander@245: { dvander@245: Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); dvander@245: hir_ = new (pool_) HBoolean(node, type, node->token() == TOK_TRUE); dvander@245: } dvander@245: dvander@245: void dvander@195: SemanticAnalysis::visitIntegerLiteral(IntegerLiteral *lit) dvander@195: { dvander@195: Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); dvander@221: hir_ = new (pool_) HInteger(lit, type, lit->value()); dvander@195: } dvander@195: dvander@195: void dvander@195: SemanticAnalysis::visitFloatLiteral(FloatLiteral *node) dvander@195: { dvander@195: Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Float)); dvander@221: hir_ = new (pool_) HFloat(node, type, (float)node->value()); dvander@195: } dvander@195: dvander@195: void dvander@234: SemanticAnalysis::visitStringLiteral(StringLiteral *node) dvander@234: { dvander@235: Type *type = zone_->types()->getString(); dvander@234: hir_ = new (pool_) HString(node, type, node->string()); dvander@234: } dvander@234: dvander@234: void dvander@195: SemanticAnalysis::visitCallExpression(CallExpression *node) dvander@195: { dvander@195: HIR *callee = eval(node->callee()); dvander@195: if (!callee) dvander@195: return; dvander@195: dvander@195: if (!callee->type()->isFunction()) { dvander@25: cc_.reportError(node->pos(), Message_CalleeNotFunction); dvander@25: return; dvander@25: } dvander@168: dvander@195: Local fun(zone_, FunctionType::cast(callee->type())); dvander@195: dvander@195: if (!checkArgumentCount(fun, node->arguments()->length())) { dvander@168: cc_.reportError(node->pos(), Message_ArgumentCountMismatch); dvander@168: return; dvander@25: } dvander@14: dvander@195: if (fun->isForward()) { dvander@195: cc_.reportError(node->pos(), Message_ForwardNotImplemented, fun->name()->chars()); dvander@195: return; dvander@195: } dvander@63: dvander@195: HIRList *args = new (pool_) HIRList; dvander@55: for (unsigned i = 0; i < node->arguments()->length(); i++) { dvander@14: Expression *expression = node->arguments()->at(i); dvander@195: HIR *arg = eval(expression); dvander@195: if (!arg) dvander@14: return; dvander@14: dvander@195: Handle given = arg->type(); dvander@71: dvander@68: bool mustComputeReference; dvander@195: if (i >= fun->parameters()->length()) { dvander@68: // Special handling for natives. Arguments past the formal must dvander@68: // always have references computed. However, arrays are already dvander@195: // references for natives. dvander@195: assert(fun->isNative() && fun->isNativeVariadic()); dvander@195: mustComputeReference = !given->isReference() && dvander@195: !given->isArray(); dvander@68: } else { dvander@195: Local actual(zone_, fun->parameterAt(i)); dvander@166: dvander@247: dvander@247: if (expression->isIndexExpression() && arg->isIndex()) { dvander@247: // If the expression is the form x[y], and this produced a dvander@247: // normal array indexing operation, then we apply some dvander@247: // special rules. dvander@247: dvander@188: if (actual->isReference()) { dvander@247: // Disabled, since this doesn't have easily definable or dvander@247: // sensible semantics with SP2 arrays, but we don't want dvander@247: // to silently break SP1 semantics. dvander@188: cc_.reportError(expression->pos(), Message_CannotComputeIndexRef); dvander@188: return; dvander@188: } dvander@247: dvander@247: // As a special rule to account for SP1 semantics, we allow x[y] dvander@247: // to automatically compute a sliced, read-only array view if dvander@247: // the desired type is an array of one more dimension than x[y] dvander@247: // would return. dvander@247: // dvander@247: // As an additional restriction, this is only allowed for dvander@247: // one-level char arrays, to minimize potential issues until dvander@247: // we determine whether more compatibility is needed. dvander@247: // dvander@247: // Internally, for sanity, the dependent array operation does dvander@247: // not necessarily create a dependent array. For extensible dvander@247: // arrays, we instead create a duplicate arrary of just the dvander@247: // sliced portion, rather than deal with the complexity of the dvander@247: // source array being resized. dvander@247: // dvander@247: // Note that we don't really know whether a dependent array dvander@247: // will be dependent at compile-time, so the dependent type's dvander@247: // just inherits from the source. dvander@247: if (actual->isArray()) { dvander@247: HIndex *index = arg->toIndex(); dvander@247: Local basetype(zone_, ArrayType::cast(index->base()->type())); dvander@247: Local atype(zone_, ArrayType::cast(actual)); dvander@247: dvander@247: if (atype->isCharArray() && basetype->isCharArray()) { dvander@247: // There is no possible way this will type check, so dvander@247: // it's safe to just try something completely dvander@247: // different. dvander@247: Local slicetype(zone_); dvander@247: if (basetype->isFixedLength()) { dvander@247: // If the base type is fixed length, we need to dvander@247: // compute a new dynamic array type based on the dvander@247: // fixed array's contents and qualifiers. dvander@247: Local contained(zone_, basetype->contained()); dvander@247: slicetype = zone_->types()->newArray(contained, ArrayType::DYNAMIC_ARRAY_SIZE); dvander@247: if (!slicetype) dvander@247: return; dvander@247: slicetype = zone_->types()->qualify(slicetype, basetype->quals()); dvander@247: if (!slicetype) dvander@247: return; dvander@247: } else { dvander@247: slicetype = basetype; dvander@247: } dvander@247: arg = new (pool_) HNewDependentArray(expression, slicetype, index->base(), index->index()); dvander@247: given = arg->type(); dvander@247: } dvander@247: } dvander@166: } dvander@166: dvander@222: if (actual->isReference()) { dvander@222: // If the actual is a reference, the coercion rules are much dvander@222: // stricter; the contained types must be identical. dvander@222: // :TODO: make this not an assert dvander@222: assert(ReferenceType::cast(actual)->contained() == arg->type()); dvander@222: } else { dvander@222: // When we start overloading, coercion will have to happen later. dvander@222: arg = coerce(arg, actual, Coerce_Arg); dvander@222: if (!arg) dvander@222: return; dvander@222: } dvander@195: dvander@68: mustComputeReference = actual->isReference() && !given->isReference(); dvander@68: } dvander@14: dvander@230: if (mustComputeReference) { dvander@230: // Refs always use the LIFO stack if they can. dvander@222: arg = new (pool_) HToRef(arg->node(), arg->type(), arg); dvander@230: requireLifoStack(); dvander@230: } dvander@195: dvander@195: if (!args->append(arg)) dvander@195: return; dvander@14: } dvander@66: dvander@221: hir_ = new (pool_) HCall(node, fun->returnType(), callee, args); dvander@172: } dvander@172: dvander@172: void dvander@195: SemanticAnalysis::visitAssignment(Assignment *node) dvander@71: { dvander@195: HLValue *lval = lvalue(node->lvalue()); dvander@195: if (!lval) dvander@71: return; dvander@71: dvander@241: if (lval->type()->quals() & TypeQual_Const) { dvander@241: cc_.reportError(node->pos(), Message_CannotModifyConst); dvander@241: return; dvander@241: } dvander@241: dvander@195: HIR *hir = eval(node->expression()); dvander@246: if (!hir) dvander@246: return; dvander@195: if ((hir = coerce(hir, lval->type(), Coerce_Assign)) == NULL) dvander@195: return; dvander@71: dvander@221: hir_ = new (pool_) HStore(node, node->token(), lval, hir); dvander@71: } dvander@71: dvander@208: void dvander@208: SemanticAnalysis::visitBreakStatement(BreakStatement *node) dvander@208: { dvander@208: if (!loop_) { dvander@208: cc_.reportError(node->pos(), Message_BreakOutsideLoop); dvander@208: return; dvander@208: } dvander@208: dvander@232: loop_->setBreaks(); dvander@230: __ j(OP_BREAK, loop_->break_()); dvander@208: } dvander@208: dvander@208: void dvander@208: SemanticAnalysis::visitContinueStatement(ContinueStatement *node) dvander@208: { dvander@208: if (!loop_) { dvander@208: cc_.reportError(node->pos(), Message_ContinueOutsideLoop); dvander@208: return; dvander@208: } dvander@208: dvander@230: __ j(OP_CONTINUE, loop_->continue_()); dvander@208: } dvander@208: dvander@212: void dvander@212: SemanticAnalysis::visitIndexExpression(IndexExpression *node) dvander@212: { dvander@212: HIR *left = eval(node->left()); dvander@212: if (!left) dvander@212: return; dvander@212: dvander@212: HIR *right = eval(node->right()); dvander@212: if (!right) dvander@212: return; dvander@212: dvander@212: // Hrm... yes, for now, let's coerce here. dvander@212: Local i32(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); dvander@212: if ((right = coerce(right, i32, Coerce_Arg)) == NULL) dvander@212: return; dvander@212: dvander@212: // Do not coerce here, there's no reason to yet. dvander@212: if (!left->type()->isArray()) { dvander@212: cc_.reportError(node->left()->pos(), Message_IndexBaseMustBeArray); dvander@212: return; dvander@212: } dvander@212: dvander@221: hir_ = new (pool_) HIndex(node, dvander@221: ArrayType::cast(left->type())->contained(), dvander@212: left, dvander@212: right); dvander@212: } dvander@212: dvander@217: void dvander@217: SemanticAnalysis::visitIncDecExpression(IncDecExpression *node) dvander@217: { dvander@217: HLValue *lval = lvalue(node->expression()); dvander@217: if (!lval) dvander@217: return; dvander@217: dvander@217: if (!lval->type()->isInt32() && !lval->type()->isFloat()) { dvander@217: assert(false); dvander@217: return; dvander@217: } dvander@217: dvander@217: HIR *rval; dvander@217: if (lval->type()->isInt32()) dvander@221: rval = new (pool_) HInteger(node, lval->type(), 1); dvander@217: else dvander@221: rval = new (pool_) HFloat(node, lval->type(), 1.0); dvander@217: dvander@218: if (node->postfix()) dvander@221: hir_ = new (pool_) HPostIncDec(node, node->token(), lval, rval); dvander@218: else dvander@221: hir_ = new (pool_) HStore(node, node->token(), lval, rval); dvander@217: } dvander@217: dvander@195: Type * dvander@195: SemanticAnalysis::coercionType(TokenKind token, HIR *left, HIR *right) dvander@0: { dvander@247: Local l(zone_, left->type()); dvander@247: Local r(zone_, right->type()); dvander@195: dvander@247: if (l->isPod()) dvander@247: l = l->unqualified(); dvander@247: if (r->isPod()) dvander@247: r = r->unqualified(); dvander@247: dvander@247: if ((token >= TOK_PLUS && token <= TOK_SLASH) || dvander@247: (token >= TOK_EQUALS && token <= TOK_GE)) dvander@247: { dvander@195: // We allow: [float|int32] + [float|int32] dvander@195: if (l->isFloat() && r->isInt32()) dvander@195: return l; dvander@195: if (l->isInt32() && r->isFloat()) dvander@195: return r; dvander@195: if (l->isInt32() && r->isInt32()) dvander@195: return l; dvander@247: dvander@247: if (l->isChar() && r->isInt32()) dvander@247: return r; dvander@247: if (l->isInt32() && r->isChar()) dvander@247: return l; dvander@195: } dvander@25: dvander@222: // :TODO: remove dvander@251: assert(*l == *r); dvander@195: dvander@195: return left->type(); dvander@195: } dvander@25: dvander@234: HIR * dvander@220: SemanticAnalysis::typeCheckArray(const SourcePosition &pos, dvander@234: HIR *from, dvander@241: Handle argActual, dvander@220: CoercionKind kind) dvander@220: { dvander@234: Local argGiven(zone_, from->type()); dvander@234: dvander@234: // Special case: if we receive a string literal, and this is a char array, dvander@234: // we allow a coercion. dvander@234: if (from->isString() && dvander@234: argGiven->isString() && dvander@234: argActual->isArray() && dvander@234: ArrayType::cast(argActual)->isCharArray()) dvander@234: { dvander@234: HString *hir = from->toString(); dvander@234: Local actual(zone_, ArrayType::cast(argActual)); dvander@234: dvander@236: if (actual->isFixedLength()) { dvander@234: if (hir->string()->length() + 1 > actual->fixedLength()) { dvander@234: cc_.reportError(pos, Message_ArrayAssignmentTooBig); dvander@234: return NULL; dvander@234: } dvander@234: } dvander@234: dvander@234: // Transform the string literal into an array literal. We give it the dvander@234: // type of the left-hand side. The constness of the destination type dvander@234: // will determine whether HArray duplicates it or not. dvander@256: Local map(zone_, actual->newMap()); dvander@234: if (!map) dvander@234: return NULL; dvander@234: dvander@234: Local array(zone_); dvander@234: array = Array::NewString(zone_, dvander@234: map, dvander@234: hir->string()->chars(), dvander@234: hir->string()->length(), dvander@234: Heap::Tenure_Old); dvander@234: if (!array) dvander@234: return NULL; dvander@234: dvander@236: // Since we're creating a canonical copy of the char array, we have to dvander@236: // be careful that it doesn't get mutated. There are two cases where dvander@236: // we know it can't be mutated, and therefore don't need to specify dvander@236: // that it be cloned after pushing it onto the stack. dvander@236: // dvander@236: // (1) The array type is const. dvander@236: // (2) The array type is fixed-length, and the coercion is for dvander@236: // assignment, meaning it will be immediately copied. dvander@236: // dvander@236: // We treat declaration separately from assignment, since for dvander@236: // declaration we can just immediately use the cloned version. dvander@236: bool shouldDuplicate = true; dvander@241: if ((actual->quals() & TypeQual_Const) || dvander@236: (actual->isFixedLength() && kind == Coerce_Assign)) dvander@236: { dvander@236: shouldDuplicate = false; dvander@236: } dvander@236: dvander@236: return new (pool_) HArray(hir->node(), actual, array, shouldDuplicate); dvander@234: } dvander@234: dvander@220: if (!argGiven->isArray()) { dvander@220: cc_.reportError(pos, Message_ExpectedArray); dvander@234: return NULL; dvander@220: } dvander@220: dvander@220: Local actual(zone_, ArrayType::cast(argActual)); dvander@220: Local given(zone_, ArrayType::cast(argGiven)); dvander@220: dvander@220: if (actual->levels() != given->levels()) { dvander@220: cc_.reportError(pos, Message_ArrayDimensionsMismatch); dvander@234: return NULL; dvander@220: } dvander@220: dvander@220: unsigned level = 1; dvander@220: while (true) { dvander@220: if (actual->isFixedLength()) { dvander@236: if (!given->isFixedLength()) { dvander@236: cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); dvander@236: return NULL; dvander@236: } dvander@236: dvander@236: if (actual->isCharArray()) { dvander@236: // String assignment can have a truncated |given|. dvander@236: if (given->fixedLength() > actual->fixedLength()) { dvander@236: cc_.reportError(pos, Message_ArrayAssignmentTooBig); dvander@234: return NULL; dvander@220: } dvander@236: } else { dvander@236: if (given->fixedLength() != actual->fixedLength()) { dvander@236: cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); dvander@236: return NULL; dvander@220: } dvander@220: } dvander@220: } else if (given->isFixedLength()) { dvander@220: // We do not allow assignments in the form: dvander@220: // [][] = [][Y] dvander@220: // dvander@220: // Otherwise, the following code could break the size guarantees dvander@220: // of the right-hand side: dvander@220: // dvander@220: // new x[][5]; dvander@220: // new y[][]; dvander@220: // new z[3]; dvander@220: // dvander@220: // dvander@220: // y = x; dvander@220: // y[0] = z; dvander@220: // dvander@220: // Now |x[0]| contains a 3-length array. dvander@220: if (level > 1) { dvander@220: cc_.reportError(pos, Message_ArrayDimensionSizeMismatch, level); dvander@234: return NULL; dvander@220: } dvander@220: } dvander@220: dvander@220: if (!given->contained()->isArray() || !actual->contained()->isArray()) dvander@220: break; dvander@220: dvander@220: given = ArrayType::cast(given->contained()); dvander@220: actual = ArrayType::cast(actual->contained()); dvander@220: } dvander@220: dvander@241: bool givenConst = !!(given->contained()->quals() & TypeQual_Const); dvander@241: bool actualConst = !!(actual->contained()->quals() & TypeQual_Const); dvander@241: dvander@220: // These arrays must contain the same innermost type. dvander@241: if (given->contained()->unqualified() != actual->contained()->unqualified() || dvander@241: (givenConst && !actualConst)) dvander@241: { dvander@241: char aname[255]; dvander@241: char gname[255]; dvander@220: cc_.reportError(pos, Message_TypeMismatch, dvander@241: GetTypeName(argActual, aname, sizeof(aname)), dvander@241: GetTypeName(argGiven, gname, sizeof(gname))); dvander@244: return NULL; dvander@220: } dvander@220: dvander@234: return from; dvander@220: } dvander@220: dvander@195: HIR * dvander@233: SemanticAnalysis::typeFailure(const SourcePosition &pos, Handle from, Handle to) dvander@233: { dvander@241: char fname[255]; dvander@241: char tname[255]; dvander@233: cc_.reportError(pos, Message_InvalidCoercion, dvander@241: GetTypeName(from, fname, sizeof(fname)), dvander@241: GetTypeName(to, tname, sizeof(tname))); dvander@233: return NULL; dvander@233: } dvander@233: dvander@233: HIR * dvander@195: SemanticAnalysis::coerce(HIR *hir, Handle to, CoercionKind kind) dvander@195: { dvander@234: Local from(zone_, hir->type()); dvander@71: dvander@256: // If the source is *ever* an importable, we just fail. dvander@256: if (from->isImportable()) dvander@256: return typeFailure(hir->node()->pos(), from, to); dvander@256: dvander@195: if (from->isInt32() && to->isFloat()) dvander@221: return new (pool_) HConvert(hir->node(), to, OP_CVT_I2F, hir); dvander@208: if (from->isInt32() && to->isBool()) dvander@221: return new (pool_) HConvert(hir->node(), to, OP_CVT_I2B, hir); dvander@233: dvander@234: if (to->isEnum() && *from != to) dvander@233: return typeFailure(hir->node()->pos(), from, to); dvander@195: dvander@241: if (to->isArray()) { dvander@241: Local atype(zone_, ArrayType::cast(to)); dvander@241: return typeCheckArray(hir->node()->pos(), hir, atype, kind); dvander@241: } dvander@195: dvander@195: return hir; dvander@0: } dvander@0: dvander@0: void dvander@195: SemanticAnalysis::set(VariableSymbol *sym) dvander@0: { dvander@195: if (sym->scope()->kind() == Scope::GLOBAL) { dvander@195: __ setglobal(sym); dvander@195: } else { dvander@195: assert(sym->storage() == VariableSymbol::Local); dvander@195: __ setlocal(sym); dvander@0: } dvander@0: } dvander@2: dvander@195: HIR * dvander@221: SemanticAnalysis::binary(AstNode *node, TokenKind token, HIR *left, HIR *right) dvander@148: { dvander@195: Local coercion(zone_, coercionType(token, left, right)); dvander@195: if (!coercion) dvander@195: return NULL; dvander@195: if ((left = coerce(left, coercion, Coerce_Arg)) == NULL) dvander@195: return NULL; dvander@195: if ((right = coerce(right, coercion, Coerce_Arg)) == NULL) dvander@195: return NULL; dvander@195: dvander@195: switch (token) { dvander@195: case TOK_PLUS: dvander@195: case TOK_MINUS: dvander@195: case TOK_STAR: dvander@195: case TOK_SLASH: dvander@195: case TOK_PERCENT: dvander@195: case TOK_AMPERSAND: dvander@195: case TOK_BITOR: dvander@195: case TOK_BITXOR: dvander@195: case TOK_SHR: dvander@195: case TOK_USHR: dvander@195: case TOK_SHL: dvander@221: return new (pool_) HBinary(node, left->type(), token, left, right); dvander@148: dvander@195: case TOK_EQUALS: dvander@195: case TOK_NOTEQUALS: dvander@195: case TOK_LT: dvander@195: case TOK_LE: dvander@195: case TOK_GT: dvander@195: case TOK_GE: dvander@195: case TOK_AND: dvander@195: case TOK_OR: dvander@195: { dvander@195: Local result(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); dvander@221: return new (pool_) HBinary(node, result, token, left, right); dvander@195: } dvander@148: dvander@195: default: dvander@195: assert(false); dvander@195: return NULL; dvander@148: } dvander@195: } dvander@148: dvander@245: static inline bool dvander@245: IsValidUncheckedType(Type *type) dvander@245: { dvander@245: return type->isEnum() || dvander@245: type->isFloat() || dvander@245: type->isInt32() || dvander@245: type->isChar() || dvander@245: type->isUnchecked() || dvander@245: type->isBool(); dvander@245: } dvander@245: dvander@245: void dvander@245: SemanticAnalysis::visitUnaryExpression(UnaryExpression *node) dvander@245: { dvander@246: HIR *hir = eval(node->expression()); dvander@246: if (!hir) dvander@246: return; dvander@246: dvander@245: switch (node->token()) { dvander@247: case TOK_TILDE: dvander@247: { dvander@247: if (!hir->type()->isInt32() && !hir->type()->isEnum()) { dvander@247: char name[255]; dvander@247: cc_.reportError(node->pos(), Message_InvalidUnaryType, dvander@247: "~", dvander@247: GetTypeName(hir->type(), name, sizeof(name))); dvander@247: } dvander@247: Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); dvander@247: hir_ = new (pool_) HUnary(node, hir->type(), TOK_TILDE, hir); dvander@247: return; dvander@247: } dvander@247: dvander@246: case TOK_MINUS: dvander@245: { dvander@247: if (hir->type()->isInt32() || hir->type()->isEnum()) { dvander@247: Local type(zone_, zone_->types()->getPrimitive(PrimitiveType_Int32)); dvander@247: hir_ = new (pool_) HUnary(node, type, TOK_MINUS, hir); dvander@247: return; dvander@247: } dvander@247: if (hir->type()->isFloat()) { dvander@247: hir_ = new (pool_) HUnary(node, hir->type(), TOK_MINUS, hir); dvander@247: return; dvander@247: } dvander@245: dvander@247: char name[255]; dvander@247: cc_.reportError(node->pos(), Message_InvalidUnaryType, dvander@247: "-", dvander@247: GetTypeName(hir->type(), name, sizeof(name))); dvander@247: break; dvander@246: } dvander@246: dvander@246: default: dvander@246: { dvander@246: assert(node->token() == TOK_LABEL); dvander@245: dvander@246: TypeSymbol *sym = node->tag()->sym()->asType(); dvander@246: if (!sym) { dvander@246: cc_.reportError(node->pos(), Message_IdentifierIsNotAType, dvander@246: sym->name()->chars()); dvander@246: return; dvander@246: } dvander@246: dvander@246: if (!IsValidUncheckedType(sym->type())) { dvander@246: char name[255]; dvander@246: cc_.reportError(node->pos(), Message_InvalidUncheckedCastTo, dvander@246: GetTypeName(hir->type(), name, sizeof(name))); dvander@246: return; dvander@246: } dvander@246: if (!IsValidUncheckedType(hir->type())) { dvander@246: char name[255]; dvander@246: cc_.reportError(node->pos(), Message_InvalidUncheckedCastFrom, dvander@246: GetTypeName(hir->type(), name, sizeof(name))); dvander@246: return; dvander@246: } dvander@246: dvander@246: hir_ = new (pool_) HUnary(node, hir->type(), TOK_LABEL, hir); dvander@246: break; dvander@245: } dvander@245: } dvander@245: } dvander@245: dvander@256: void dvander@256: SemanticAnalysis::import(HIR *hir, FieldExpression *node) dvander@256: { dvander@256: // Note that we don't care if the hir type is "importable", since we dvander@256: // can only do anything if the important has a constant binding. dvander@256: unsigned index = hir->toImport()->index(); dvander@256: Local importable(zone_, unit_->module()->getImportAt(index)); dvander@256: dvander@256: if (importable->isPackage()) { dvander@256: // Look at what's already loaded and see if anything exists. We dvander@256: // can't create stuff on-demand here since it's too late to add dvander@256: // new translation units to the CompileContext. dvander@256: Local package(zone_, Package::cast(importable)); dvander@256: Local found(zone_, package->import(node->field())); dvander@256: if (!found) { dvander@256: cc_.reportError(node->pos(), Message_PackageDoesNotHaveMember, node->field()->chars()); dvander@256: return; dvander@256: } dvander@256: dvander@256: // If we did find something, make sure it is in our import list. dvander@256: // If we didn't explicitly import it, we have no right to use it. dvander@256: for (index = 0; index < unit_->module()->imports(); index++) { dvander@256: if (unit_->module()->getImportAt(index) == importable) dvander@256: break; dvander@256: } dvander@256: dvander@256: if (index == unit_->module()->imports()) { dvander@256: cc_.reportError(node->pos(), Message_ImportWasNeverImported, node->field()->chars()); dvander@256: return; dvander@256: } dvander@256: dvander@256: // Just create a new HImport. dvander@256: hir_ = new (pool_) HImport(node, hir->type(), index); dvander@256: } else { dvander@256: // Modules can never have sub-modules or sub-packages. dvander@256: unsigned index; dvander@256: Local module(zone_, Module::cast(importable)); dvander@256: Local map(zone_, module->globals()->map()); dvander@256: Local desc(zone_, map->type()->lookupField(node->field(), &index)); dvander@256: if (!desc) { dvander@256: cc_.reportError(node->pos(), Message_FieldNotFound, node->field()->chars()); dvander@256: return; dvander@256: } dvander@256: hir_ = new (pool_) HField(node, hir, desc->type(), index); dvander@256: } dvander@256: } dvander@256: dvander@256: void dvander@256: SemanticAnalysis::visitFieldExpression(FieldExpression *node) dvander@256: { dvander@256: HIR *hir = eval(node->left()); dvander@256: if (!hir) dvander@256: return; dvander@256: dvander@256: if (hir->isImport()) { dvander@256: import(hir, node); dvander@256: return; dvander@256: } dvander@256: dvander@256: char name[255]; dvander@256: cc_.reportError(node->pos(), Message_InvalidFieldExpression, dvander@256: GetTypeName(hir->type(), name, sizeof(name))); dvander@256: } dvander@256: dvander@195: static inline TokenKind dvander@195: ToCompare(Expression *expr) dvander@195: { dvander@195: if (expr->isBinaryExpression()) { dvander@195: BinaryExpression *b = expr->toBinaryExpression(); dvander@195: if (b->token() >= TOK_EQUALS && b->token() <= TOK_OR) dvander@195: return b->token(); dvander@195: } else if (expr->isUnaryExpression()) { dvander@195: UnaryExpression *u = expr->toUnaryExpression(); dvander@195: if (u->token() == TOK_NOT) dvander@195: return TOK_NOT; dvander@195: } dvander@195: return TOK_EOF; dvander@148: } dvander@148: dvander@148: void dvander@195: SemanticAnalysis::visitForTest(Expression *expr, dvander@195: Label *trueBranch, dvander@195: Label *falseBranch, dvander@195: Label *fallthrough) dvander@2: { dvander@208: Local boolType(zone_, zone_->types()->getPrimitive(PrimitiveType_Bool)); dvander@208: dvander@195: TokenKind token = ToCompare(expr); dvander@195: if (token == TOK_EOF || token == TOK_NOT) { dvander@208: HIR *hir; dvander@208: if (token == TOK_EOF) dvander@208: hir = eval(expr); dvander@208: else dvander@208: hir = eval(expr->toUnaryExpression()->expression()); dvander@208: if (!hir) dvander@208: return; dvander@234: if ((hir = coerce(hir, boolType, Coerce_Arg)) == NULL) dvander@208: return; dvander@208: dvander@208: discharge(hir); dvander@208: dvander@208: bool jumpOnTrue = (fallthrough == falseBranch) ^ (token == TOK_NOT); dvander@208: Opcode op = jumpOnTrue ? OP_JT : OP_JF; dvander@208: dvander@208: if (fallthrough == falseBranch) dvander@208: __ j(op, trueBranch); dvander@208: else dvander@208: __ j(op, falseBranch); dvander@172: return; dvander@172: } dvander@172: dvander@195: BinaryExpression *bin = expr->toBinaryExpression(); dvander@195: if (token == TOK_AND || token == TOK_OR) { dvander@247: Label next; dvander@247: if (token == TOK_AND) dvander@247: visitForTest(bin->left(), &next, falseBranch, &next); dvander@247: else dvander@247: visitForTest(bin->left(), trueBranch, &next, &next); dvander@247: __ bind(&next); dvander@247: visitForTest(bin->right(), trueBranch, falseBranch, fallthrough); dvander@195: return; dvander@172: } dvander@172: dvander@195: HIR *left = eval(bin->left()); dvander@195: HIR *right = eval(bin->right()); dvander@195: if (!left || !right) dvander@195: return; dvander@195: dvander@221: HIR *hir = binary(bin, bin->token(), left, right); dvander@195: if (!hir) dvander@195: return; dvander@234: if ((hir = coerce(hir, boolType, Coerce_Arg)) == NULL) dvander@208: return; dvander@172: dvander@195: discharge(hir); dvander@195: if (fallthrough == trueBranch) dvander@195: __ j(OP_JF, falseBranch); dvander@195: else dvander@195: __ j(OP_JT, trueBranch); dvander@195: } dvander@195: dvander@195: HLValue * dvander@195: SemanticAnalysis::lvalue(Expression *expr) dvander@195: { dvander@195: HIR *hir = eval(expr); dvander@195: if (!hir) dvander@195: return NULL; dvander@195: dvander@195: if (!hir->isLValue()) { dvander@195: cc_.reportError(expr->pos(), Message_ExpectedLValue); dvander@195: return NULL; dvander@172: } dvander@172: dvander@195: return static_cast(hir); dvander@195: } dvander@172: dvander@195: HIR * dvander@195: SemanticAnalysis::eval(Expression *expr) dvander@195: { dvander@195: assert(!hir_); dvander@195: expr->accept(this); dvander@172: dvander@195: HIR *hir = hir_; dvander@195: hir_ = NULL; dvander@195: dvander@195: return hir; dvander@172: } dvander@172: dvander@4: void dvander@195: SemanticAnalysis::discharge(HIR *hir) dvander@146: { dvander@195: EmitHIR(zone_, emitter_, hir); dvander@146: }