/* widen.cc: Definitions for member functions that add the conversions implied by type checking (mostly widening) */ #include "AST.h" #include "compiler.h" #include "decls.h" #include "utils.h" #include "optimize.h" /* (Kludge:keep state in globals) */ /* current method */ static MethodDeclNode *currentMethod; TreeNode *TreeNode::typeWiden(TypeNode *to) { if (type()->typeIdent(to)) return this; if (isPrimitiveLitNode(this) && to->isPrimitive()) { return new PrimitiveLitNode(this->literal().cast(to->kind())); } ExprNode * const cast = new CastNode(this, to, position()); cast->type(to); // cout << "typeWiden: " << pseudocode(this) << " to " << pseudocode(cast) << endl; return cast; } TreeNode *TreeNode::widenToGlobal() { TypeNode *fromType = type(); Modifiers fromMods = fromType->modifiers(); Modifiers toMods = (Modifiers) (fromMods & ~(Local | LocalInferred)); TypeNode *toType = fromType->newModifiers( toMods ); return typeWiden( toType ); } void CompileUnitNode::widen() { foriter (type, types()->allChildren(), ChildIter) (*type)->widen(); } void TemplateDeclNode::widen() { } void TreeNode::widen() { foriter (p, allChildren(), ChildIter) (*p)->widen(); _widen(); } void MethodDeclNode::widen() { currentMethod = this; TreeNode::widen(); currentMethod = NULL; } void TreeNode::_widen() { } void UnaryArithNode::_widen() { opnd0(opnd0()->typeWiden(type())); } void BinaryArithNode::_widen() { opnd0(opnd0()->typeWiden(type())); opnd1(opnd1()->typeWiden(type())); } void BinaryArithAssignNode::_widen() { opnd1(opnd1()->typeWiden(arithPromoteType(opnd0()->type(), opnd1()->type()))); } void ShiftNode::_widen() { opnd0(opnd0()->typeWiden(type())); opnd1(opnd1()->typeWiden(type())); } void ShiftAssignNode::_widen() { opnd1(opnd1()->typeWiden(arithPromoteType(opnd0()->type(), opnd1()->type()))); } void RelationNode::_widen() { TypeNode *rtype = arithPromoteType(opnd0()->type(), opnd1()->type()); opnd0(opnd0()->typeWiden(rtype)); opnd1(opnd1()->typeWiden(rtype)); } void EqualityNode::_widen() { TypeNode &type0 = *opnd0()->type(); TypeNode &type1 = *opnd1()->type(); TypeNode *rtype = 0; if (type0.isArithType()) rtype = arithPromoteType(&type0, &type1); else if (type0.isTitaniumArrayType() || type1.isTitaniumArrayType()) { if (type0.isNullType()) rtype = &type1; else if (type1.isNullType()) rtype = &type0; else rtype = type0.isLocal() ? &type1 : &type0; } else if (type0.isReference()) { static TypeNode * const globalType = ObjectDecl->asType(); static TypeNode * const localType = globalType->newModifiers(Local); const bool isLocal = type0.isLocal() && type1.isLocal(); rtype = isLocal ? localType : globalType; } if (rtype) { opnd0(opnd0()->typeWiden(rtype)); opnd1(opnd1()->typeWiden(rtype)); } } void BitwiseNode::_widen() { opnd0(opnd0()->typeWiden(type())); opnd1(opnd1()->typeWiden(type())); } void BitwiseAssignNode::_widen() { opnd1(opnd1()->typeWiden(arithPromoteType(opnd0()->type(), opnd1()->type()))); } void ArrayAccessNode::_widen() { if (index()->type()->isPrimitive()) // only for java arrays index(index()->typeWiden(theIntType)); } void ArrayInitNode::_widen() { TypeNode * const elementType = type()->elementType(); foriter (initializer, initializers()->allChildren(), ChildIter) *initializer = (*initializer)->typeWiden(elementType); } void TreeNode::methodWiden(TreeNode *args, Decl *decl) { MethodTypeNode *method = (MethodTypeNode *) decl->type(); foriter2 (argType, method->paramTypes()->allChildren(), arg, args->allChildren(), ChildIter) *arg = (*arg)->typeWiden((TypeNode *)*argType); } void TreeNode::methodWiden() { TreeNode *mthod = method(); Decl *mdecl = mthod->decl(); Modifiers mflags = mdecl->modifiers(); methodWiden(args(), mdecl); if (!(mflags & Static)) { // must be an ObjectFieldAccessNode (after rewrite...) const TypeNode &dispatchType = *mthod->object()->type(); // Check dispatching object for a local->global conversion if (!(mflags & Local)) { Modifiers dflags = dispatchType.modifiers(); if (dflags & Local) mthod->object(mthod->object()->widenToGlobal()); } if (dispatchType.isTitaniumArrayType()) { // The current Titanium array implementation requires that // any array arguments have the same local qualification as the target array // So if any of the array arguments or target array is global, then // we make the other one global as well. // Yes, this is a hack. bool mismatch = false; for (int sweep=0; sweep < args()->arity(); sweep++) if (args()->child(sweep)->type()->isTitaniumArrayType() && dispatchType.isLocal() != args()->child(sweep)->type()->isLocal()) mismatch = true; if (mismatch) { mthod->object( mthod->object()->widenToGlobal() ); for (int sweep=0; sweep < args()->arity(); sweep++) if (args()->child(sweep)->type()->isTitaniumArrayType()) args()->child( sweep, args()->child( sweep )->widenToGlobal() ); } } } } void MethodCallNode::_widen() { methodWiden(); } void MethodCallAssignNode::_widen() { methodWiden(); } void AllocateNode::_widen() { methodWiden(args(), decl()); } void ThisConstructorCallNode::_widen() { methodWiden(args(), decl()); } void SuperConstructorCallNode::_widen() { if (decl()) methodWiden(args(), decl()); } void AllocateArrayNode::_widen() { foriter (dimExpr, dimExprs()->allChildren(), TreeNode::ChildIter) if (!(*dimExpr)->expr()->type()->isRectDomainType()) (*dimExpr)->expr((*dimExpr)->expr()->typeWiden(theIntType)); } void ComplementNode::_widen() { opnd0(opnd0()->typeWiden(type())); } void PlusNode::_widen() { if (!type()->isStringType()) { opnd0(opnd0()->typeWiden(type())); opnd1(opnd1()->typeWiden(type())); } } void IfExprNode::_widen() { thenOpnd(thenOpnd()->typeWiden(type())); elseOpnd(elseOpnd()->typeWiden(type())); } void AssignNode::_widen() { opnd1(opnd1()->typeWiden(type())); } void PlusAssignNode::_widen() { if (!type()->isStringType()) opnd1(opnd1()->typeWiden(arithPromoteType(opnd0()->type(), opnd1()->type()))); } void PointNode::_widen() { foriter (expr, args()->allChildren(), TreeNode::ChildIter) *expr = (*expr)->typeWiden(theIntType); } void DomainNode::_widen() { // Handle both domain syntaxes // Could convert one into the other here, really. if (!(args()->arity() == 1 && args()->child(0)->child(0)->type()->isPointType())) foriter (range, args()->allChildren(), TreeNode::ChildIter) foriter (expr, (*range)->allChildren(), TreeNode::ChildIter) *expr = (*expr)->typeWiden(theIntType); } /* Statements */ void FieldDeclNode::_widen() { if (!initExpr()->absent()) initExpr(initExpr()->typeWiden(dtype())); } void VarDeclNode::_widen() { if (!initExpr()->absent()) initExpr(initExpr()->typeWiden(dtype())); } void SwitchNode::_widen() { expr(expr()->typeWiden(theIntType)); } void ReturnNode::_widen() { if (currentMethod) { TypeNode *rtype = currentMethod->returnType(); if (rtype->kind() != VoidKind) expr(expr()->typeWiden(rtype)); } } void ThrowNode::_widen() { // exceptions are always thrown as global references const TypeNode &type = *expr()->type(); const Modifiers modifiers = (Modifiers) (type.modifiers() & ~(Local | LocalInferred)); TypeNode * const globalType = type.newModifiers( modifiers ); expr( expr()->typeWiden(globalType ) ); } void BroadcastNode::_widen() { // widen local references into global references expr( expr()->typeWiden(type() ) ); } // The type conversions inherent in the xxxAssignNode nodes cannot be // represented by CastNode's until after the rewriting in lowering. // Instead, given a op= b, two functions are provided: // - widenSource: the type to which a should be widened to perform 'a op b' // - castResult: the type to which 'a op b' should be cast before assignment to a // In both cases, NULL is returned if no conversion is necessary. // None of these casts require run-time checks, except possibly MethodCallAssignNode. // widenSource is also the type at which 'op' is computed static TypeNode *typeWidenTo(TreeNode *from, TypeNode *to) { if (from->type()->typeIdent(to) || (from->type()->isPrimitive() && from->type()->typeIdentNM(to))) return NULL; else return to; } TypeNode *TreeNode::_widenSource() { return typeWidenTo(opnd0(), arithPromoteType(opnd0()->type(), opnd1()->type())); } TypeNode *TreeNode::_castResult() { return typeWidenTo(arithPromoteType(opnd0()->type(), opnd1()->type()), type()); } TypeNode *TreeNode::widenSource() { undefined("widenSource"); return NULL; } TypeNode *TreeNode::castResult() { undefined("castResult"); return NULL; } TypeNode *MethodCallAssignNode::widenSource() { return NULL; } TypeNode *MethodCallAssignNode::castResult() { TreeNode *assigntarget; if (isRewrittenRHSOpOverload()) assigntarget = args()->child(0); else assigntarget = method()->object(); while (isCastNode(assigntarget)) assigntarget = assigntarget->opnd0(); return typeWidenTo(method()->decl()->type()->returnType(), assigntarget->type()); } TypeNode *BinaryArithAssignNode::widenSource() { return _widenSource(); } TypeNode *BinaryArithAssignNode::castResult() { return _castResult(); } TypeNode *ShiftAssignNode::widenSource() { return _widenSource(); } TypeNode *ShiftAssignNode::castResult() { return _castResult(); } TypeNode *BitwiseAssignNode::widenSource() { return _widenSource(); } TypeNode *BitwiseAssignNode::castResult() { return _castResult(); } TypeNode *PlusAssignNode::widenSource() { if (type()->isStringType()) return NULL; else return _widenSource(); } TypeNode *PlusAssignNode::castResult() { if (type()->isStringType()) return NULL; else return _castResult(); }