/* st-field.cc: resolve fields, overloading, and do other random semantic checks */ #include "AST.h" #include "AllocateContext.h" #include "ClassContext.h" #include "FlattenContext.h" #include "NameContext.h" #include "TypeContext.h" #include "code-util.h" #include "decls.h" #include "errors.h" #include "st-field.h" /* Utilities */ // Check if types corresponding to d and crnt have the same outermost // enclosing type. bool isEnclosingAccess(Decl *d, Decl *crnt) { TreeNode *t1 = d->source(); TreeNode *t2 = crnt->source(); while (t1->enclosingType()) t1 = t1->enclosingType(); while (t2->enclosingType()) t2 = t2->enclosingType(); return t1 == t2; } static void checkFieldAccess(Decl *d, Decl *accessDecl, bool thisAccess, bool isSuper, bool isConstructor, TreeNode::FieldContext *ctx, SourcePosn p) { Decl::Modifiers m = d->modifiers(); if (thisAccess && ctx->inStatic && !(m & Common::Static)) Error(p) << "access to non-static " << d->errorName() << " in static code" << endl; if (m & Common::Private) { if (d->container() != ctx->currentClass->decl() && !isEnclosingAccess(d->container(), ctx->currentClass->decl())) Error(p) << "can't access private " << d->errorName() << endl; } else if (m & Common::Protected) { if (!(d->container()->container() == ctx->currentPackage || isSuper || (!isConstructor && isSubClass(ctx->currentClass->decl(), accessDecl)))) Error(p) << "illegal access to protected " << d->errorName() << endl; } else if (!(m & Common::Public)) // no visibility declared, i.e. package { if (d->container()->container() != ctx->currentPackage) Error(p) << "can't access package-only " << d->errorName() << " from other package" << endl; } } // Insert qualifier into the args list. static TreeListNode *rewriteArgs(TreeListNode *args, TreeNode *qualifier) { extern llist *addToFront(TreeNode *, TreeListNode *); llist *lst = addToFront(qualifier, args); return new TreeListNode(lst, args->position()); } // Check if this or an enclosing instance can be used as qualifier for an // unqualified allocation of an inner class. If so, return n if the nth // enclosing instance can be used, 0 for this, or -1 if nothing can be used. static int checkUnqualifiedAllocation(TreeNode *anode, TreeNode *dtype, TreeNode::FieldContext *ctx) { TreeNode *source = dtype->decl()->source(); TreeNode *crnt = ctx->currentClass->decl()->source(); bool inStatic = ctx->inStatic; int nesting = -1; do { nesting++; if (crnt == source->enclosingType() || isSubClass(crnt->decl(), source->enclosingType()->decl())) { if (inStatic) { anode->error() << "unqualified allocation or super constructor call of inner " << dtype->decl()->errorName() << " from a static context" << endl; return -1; } else return nesting; } if (crnt->flags() & TreeNode::Static) inStatic = true; } while ((crnt = crnt->enclosingType())); anode->error() << "unqualified allocation or super constructor call of inner " << dtype->decl()->errorName() << endl; return -1; } // Return a series of field references referring to the nth enclosing instance. static TreeNode *writeNthEncloser(TreeNode::FieldContext *ctx, SourcePosn pos, int n) { extern NameNode *buildOuterNameNode(TreeNode *, SourcePosn posn); if (n == 0) return new ThisNode(TreeNode::omitted, ctx->currentClass, ctx->thisModifiers, pos); TreeNode *field = TreeNode::omitted; for (int i = 0; i < n; i++) field = buildOuterNameNode(field, pos); bool postponed = false; TreeNode *resolved = resolveAName(field, *ctx->currentClass->decl()->environ(), ctx->currentClass, ctx->thisModifiers, ctx->currentPackage, postponed, Decl::Field); assert(!postponed); return resolved->resolveField(ctx, false); } // Check if this nested class was declared inside or is a template. Does not // assume that we disallow templated nested classes. static bool checkIfInTemplate(TreeNode *tn) { while (tn != NULL && !tn->absent()) { if (isTemplateInstanceDeclNode(tn->parent())) return true; tn = tn->enclosingType(); } return false; } TreeNode *TreeNode::resolveField(FieldContext *ctx, bool *) { const int numChildren = arity(); for (int sweep = 0; sweep < numChildren; ++sweep) child(sweep, child(sweep)->resolveField(ctx, NULL)); return _resolveOperators(); } TreeNode *CompileUnitNode::resolveField(FieldContext *, bool *) { FieldContext c; c.currentPackage = thePackage; c.currentCompileUnit = this; types()->resolveField(&c, NULL); return this; } TreeNode *TemplateDeclNode::resolveField( FieldContext *context, bool *inAssignment ) { return this; } Common::Modifiers computeConstructorSharingLUB(ClassDeclNode *cdn) { // compute LUB of constructor sharing qualifiers, // which determines sharing qualifiers on this for instance/field initializers Sharing sharingLUB = Unconstrained; foriter (constructor, cdn->members()->allChildren(), TreeNode::ChildIter) { if (isConstructorDeclNode(*constructor)) { sharingLUB = lub(sharingLUB, modifiersToSharing((*constructor)->flags())); } } #if 0 cout << *cdn->simpName()->ident() << " instance/field init this qualifier: " << sharingLUB << endl; #endif return sharingToModifiers(sharingLUB); } TreeNode *ClassDeclNode::resolveField(FieldContext *ctx, bool *inAssignment) { // If this is anonymous class, tell allocator to re-resolve if necessary. if (flags() & AnonymousClass && allocateContext()) { FieldContext *actx = allocateContext()->fieldContext(); allocateContext()->allocator->resolveField(actx, NULL); allocateContext(NULL); // no longer needed } ctx->constructorSharingLUB = computeConstructorSharingLUB(this); TypeDeclNode::resolveField( ctx, inAssignment ); // Check constructors for circularity bool change; do { change = false; foriter (constructor, members()->allChildren(), ChildIter) change = change | (*constructor)->updateConstructorValidity(); } while (change); foriter (constructor, members()->allChildren(), ChildIter) if (!(*constructor)->valid()) (*constructor)->error() << "'this' constructor calls are recursive" << endl; // Check that immutable classes have a 0-arg constructor if ((flags() & Immutable) && (!enclosingType() || (flags() & Static))) { ClassDecl &d = *decl(); EnvironIter constructors = d.environ()->lookupFirstProper(d.name(), Decl::Constructor); llist* nothing = 0; static TreeListNode *noArgs = new TreeListNode(nothing); foriter (c, constructors, EnvironIter) if (c->type()->isCallableWith(Local, noArgs)) return this; error() << "immutable " << d.errorName() << " must have a 0-argument constructor" << endl; } finalVars(TreeNode::omitted); // no longer needed return this; } bool TreeNode::updateConstructorValidity() { return false; } bool ConstructorDeclNode::updateConstructorValidity() { if (valid()) return false; Decl *called = constructorCall()->decl(); if (!called->hasSource() || // stubs must be correct called->source()->valid()) { constructorCall()->valid(true); return true; } return false; } bool ConstructorDeclNode::valid() const { return constructorCall()->valid(); } TreeNode *FieldDeclNode::resolveField(FieldContext *ctx, bool *) { FieldContext subCtx(*ctx); subCtx.inStatic = (decl()->modifiers() & Static) != 0; if (ctx->currentClass->kind() != ImmutableKind) subCtx.thisModifiers = (Modifiers) (Local | ctx->constructorSharingLUB); initExpr(initExpr()->resolveField (&subCtx, NULL)); return this; } TreeNode *StaticInitNode::resolveField(FieldContext *ctx, bool *) { FieldContext subCtx(*ctx); subCtx.inStatic = true; block()->resolveField (&subCtx, NULL); return this; } TreeNode *InstanceInitNode::resolveField(FieldContext *ctx, bool *) { FieldContext subCtx(*ctx); // Instance init always called from a constructor, so this always local Modifiers mods = Local; if (ctx->currentClass->kind() != ImmutableKind) mods = (Modifiers) (mods | ctx->constructorSharingLUB); subCtx.thisModifiers = mods; block()->resolveField (&subCtx, NULL); return this; } TreeNode *MethodDeclNode::resolveField(FieldContext *ctx, bool *) { FieldContext subCtx(*ctx); const Modifiers localBit = ctx->currentClass->isImmutable() ? Local : None; subCtx.thisModifiers = (Modifiers) (flags() | localBit); subCtx.inStatic = (decl()->modifiers() & Static) != 0; params()->resolveField (&subCtx, NULL); body()->resolveField (&subCtx, NULL); return this; } TreeNode *ConstructorDeclNode::resolveField(FieldContext *ctx, bool *) { FieldContext subCtx(*ctx); subCtx.thisModifiers = flags(); if (simpName()->ident() != subCtx.currentClass->decl()->name()) simpName()->error () << "constructor for " << subCtx.currentClass->decl()->errorName() << " must be named " << *subCtx.currentClass->decl()->declaredName() << endl; params()->resolveField (&subCtx, NULL); initEncloser(initEncloser()->resolveField(&subCtx, NULL)); constructorCall(constructorCall()->resolveField (&subCtx, NULL)); body()->resolveField (&subCtx, NULL); // Anonymous constructors inherit throws clauses from super constructor. if ((ctx->currentClass->decl()->modifiers() & AnonymousClass) && (flags() & CompilerGenerated)) { throws(constructorCall()->decl()->source()->throws()->deepClone()); decl()->type()->throws(throws()); } return this; } TreeNode *ThisConstructorCallNode::resolveField(FieldContext *ctx, bool *) { EnvironIter methods = ctx->currentClass->decl()->environ()->lookupFirstProper (ctx->currentClass->decl()->name(), Decl::Constructor); args()->resolveField (ctx, NULL); decl(resolveCall(methods, ctx->thisModifiers, args(), position())); // no access check needed (the errors checked for can't occur) return this; } TreeNode *SuperConstructorCallNode::resolveField(FieldContext *ctx, bool *) { Decl *super = ctx->currentClass->decl()->superClass(); if (!super) { /* Generated calls to super are distinguishable from user-written ones via the CompilerGenerated flag. */ if (!(flags() & CompilerGenerated)) error() << "Immutable class constructors cannot call a super-class constructor" << endl; return TreeNode::omitted; } EnvironIter methods = super->environ()->lookupFirstProper (super->name(), Decl::Constructor); qualifier(qualifier()->resolveField (ctx, NULL)); args()->resolveField(ctx, NULL); TreeNode *ctype = ctx->currentClass; TreeNode *stype = ctype->decl()->superClass()->asType(); if (qualifier() != NULL && !qualifier()->absent()) { if ((!qualifier()->type()->isReference() && !qualifier()->type()->isImmutable()) || qualifier()->type()->isNullType()) { error() << qualifier()->type()->decl()->errorName() << " not allowed in qualified super constructor call" << endl; qualifier(new NullPntrNode(qualifier()->position())); } // translate to Java 1.0 (enclosing instance is first arg to constructor) if (stype->decl()->source()->hasEnclosingInstance()) args(rewriteArgs(args(), qualifier())); else error() << "qualified super constructor call of non-inner " << stype->decl()->errorName() << endl; qualifier(TreeNode::omitted); } else if (stype->decl()->source()->hasEnclosingInstance()) { // check if this or enclosing instance can be used as qualifier int nesting = checkUnqualifiedAllocation(this, stype, ctx); if (nesting < 0) args(rewriteArgs(args(), new NullPntrNode(position()))); else args(rewriteArgs(args(), writeNthEncloser(ctx, position(), nesting))); } decl(resolveCall(methods, ctx->thisModifiers, args(), position())); checkFieldAccess(decl(), ctx->currentClass->decl()->superClass(), true, true, true, ctx, position()); return this; } TreeNode *TypeDeclNode::resolveField(FieldContext *ctx, bool *) { FieldContext subCtx(*ctx); subCtx.currentClass = decl()->asType(); members()->resolveField(&subCtx, NULL); return this; } Decl *TreeNode::resolveAField(TypeDecl &typeDecl) { if (&typeDecl == UnknownClassDecl) return UnknownFieldDecl; else { Decl *d = decl(); if (!d) // don't repeat work { EnvironIter resolutions = typeDecl.environ() ->lookupFirstProper (simpName()->ident(), Decl::Field); bool more = moreThanOne (resolutions); if (resolutions.isDone()) { error () << "no " << *simpName()->ident() << " field in " << typeDecl.errorName() << endl; d = UnknownFieldDecl; } else { d = &*resolutions; if (more) error() << "ambiguous reference to " << d->errorName(); } } return d; } } void TreeNode::resolveAMember(bool thisAccess, bool isSuper, FieldContext *ctx) { TypeNode &oType = *accessedObjectType(); TypeDecl &typeDecl = *oType.decl(); Decl *d; if (!ctx->methodArgs) d = resolveAField(typeDecl); else { EnvironIter resolutions = typeDecl.environ() -> lookupFirstProper (simpName()->ident(), Decl::Method); if (resolutions.isDone()) { // avoid duplicate error messages for errors already reported by // resolveName if (!decl()) error() << "no " << *simpName()->ident() << " method in " << typeDecl.errorName() << endl; d = UnknownMethodDecl; } else d = resolveCall(resolutions, oType.modifiers(), ctx->methodArgs, position()); } simpName()->decl(d); checkFieldAccess(d, &typeDecl, thisAccess, isSuper, false, ctx, position()); } TreeNode *ObjectFieldAccessNode::resolveField(FieldContext *ctx, bool *) { FieldContext subCtx(*ctx); subCtx.methodArgs = NULL; object(object()->resolveField (&subCtx, NULL)); /* DOB: PR538 - SFAN's get rewritten into an OFAN after field * resolution. Don't attempt to re-do field resolution on those because * we may get the wrong answer (in a few cases field resolution is invoked * to resolve new nodes during rewriting) */ if (isRewrittenSFAN()) return this; if (isRewrittenQSFAN()) isRewrittenSFAN(true); TypeNode *ot = object()->type(); if (!(ot->isReference() || ot->isImmutable())) { if (!decl()) error() << "attempt to select from non-reference type " << ot->typeName() << endl; simpName()->decl(ctx->methodArgs ? UnknownMethodDecl : UnknownFieldDecl); } else resolveAMember(false, false, ctx); if (isThisNode(object())) return new ThisFieldAccessNode(object()->theClass(), object()->flags(), simpName(), position()); else return this; } TreeNode *TypeFieldAccessNode::resolveField(FieldContext *ctx, bool *) { resolveAMember(true, false, ctx); if (!(decl()->modifiers() & Static)) error() << "must specify object when accessing " << decl()->errorName() << endl; return this; } TreeNode *ThisFieldAccessNode::resolveField(FieldContext *ctx, bool *) { resolveAMember(true, false, ctx); // Rewrite static accesses that were temporarily rewritten as this // field accesses. if (decl()->modifiers() & Static) return new TypeFieldAccessNode((TypeNode *) theClass(), simpName(), position()); return this; } TreeNode *SuperFieldAccessNode::resolveField(FieldContext *ctx, bool *) { if (ctx->inStatic) { error() << "cannot use 'super' in static code" << endl; simpName()->decl(ctx->methodArgs ? UnknownMethodDecl : UnknownFieldDecl); } else resolveAMember(true, true, ctx); return this; } TreeNode *MethodCallNode::resolveField(FieldContext *ctx, bool *) { args()->resolveField (ctx, NULL); FieldContext subCtx(*ctx); subCtx.methodArgs = args(); method(method()->resolveField (&subCtx, NULL)); return this; } TreeNode *ThisNode::resolveField(FieldContext *ctx, bool *) { if (ctx->inStatic) error() << "cannot use 'this' in static code" << endl; return this; } TreeNode *AllocateNode::resolveField(FieldContext *ctx, bool *) { Decl *constructor = UnknownMethodDecl; if (_fieldResolutions == 0) { region(region()->resolveField(ctx, NULL)); qualifier(qualifier()->resolveField (ctx, NULL)); dtype()->resolveField (ctx, NULL); args()->resolveField (ctx, NULL); } else if (_fieldResolutions > 1) { // This is not qualified anonymous class. It need not be re-resolved. return this; } else { // Get rid of qualifier to avoid confusing re-resolution. qualifier(TreeNode::omitted); } // Anonymous allocation. Now we have enough type information to know // what types the anonymous constructor should take in. if (!cbody()->absent()) { extern TypeNode *UnknownType; TreeNode *cnstr = cbody()->members()->child(2); // default constructor TreeListNode *cnstrArgs = cnstr->params(); for (int i = cnstrArgs->arity()-1, j = args()->arity()-1; i >= 0 && j >= 0; i--, j--) { if (cnstrArgs->child(i)->dtype() == UnknownType) cnstrArgs->child(i)->dtype(args()->child(j)->type()->deepClone()); } // Do deferred class and name resolution of anonymous constructor, if // this is not qualified anonymous class on the first pass. if (qualifier()->absent() || (_fieldResolutions == 1)) { bool postponed = false; ClassContext cctx(postponed); NameContext nctx(postponed); cctx.package = nctx.currentPackage = ctx->currentPackage; cctx.cclass = static_cast(cbody()->decl()); nctx.currentClass = cbody()->decl()->asType(); cctx.fileEnv = nctx.env = cbody()->decl()->environ(); cnstr->resolveClass(&cctx); cnstr->resolveName(nctx); cbody(TreeNode::omitted); assert(!postponed); } else { // Save allocation context so resolution can be done later. cbody()->allocateContext(new AllocateContext(ctx, this)); } } // qualified allocation if (qualifier() != NULL && !qualifier()->absent()) { if ((!qualifier()->type()->isReference() && !qualifier()->type()->isImmutable()) || qualifier()->type()->isNullType()) { error() << qualifier()->type()->decl()->errorName() << " not allowed in qualified allocation" << endl; qualifier(new NullPntrNode(qualifier()->position())); } else if (!dtype()->name()->qualifier()->absent()) error() << "qualified name for " << *dtype()->name()->ident() << " not allowed in qualified allocation" << endl; else { // do deferred type/field resolution on dtype() // need to lookup type of allocation in environment of qualifier type bool postponed = false; TypeContext tctx(postponed); tctx.package = ctx->currentPackage; tctx.cclass = ctx->currentClass; tctx.fileEnv = ctx->currentPackage->environ(); tctx.typeEnv = qualifier()->type()->decl()->environ(false); dtype((TypeNode *) dtype()->resolveTypes(&tctx)); dtype((TypeNode *) dtype()->resolveField(ctx, NULL)); // If this is qualified anonymous class, fill in class's supertype and // move it to a new compile unit so it can undergo all resolution // phases. if (!cbody()->absent()) { extern void rewriteQualifiedAnonymousConstructor(TreeNode *, TypeNode *, bool); // Check to make sure anonymous class's supertype is actually inner class. if (!dtype()->decl()->source()->hasEnclosingInstance()) { error() << "qualified allocation of non-inner " << dtype()->decl()->errorName() << endl; // Rewrite the anonymous class's constructor to take in the qualifier // as an argument but not to pass it to the super constructor call. // This is to prevent spurious error messages. rewriteQualifiedAnonymousConstructor(cbody()->members()->child(2), qualifier()->type(), false); } else { // Rewrite the anonymous class's constructor to take in the qualifier // as an argument and to pass it as the qualifier in its super constructor // call. rewriteQualifiedAnonymousConstructor(cbody()->members()->child(2), qualifier()->type(), true); } // Fill in class's supertype. cbody()->superClass(dtype()->decl()->asType()); // Must do some resolution on new compile unit so that we can resolve // the type of the anonymous class. CompileUnitNode &innerUnit = *ctx->currentCompileUnit->clone(); llist *tmp = cons(cbody()); innerUnit.types(new TreeListNode(tmp, cbody()->position())); llist *finals = NULL; TreeNode::FlattenContext ctx(finals); ctx.toplevel = &innerUnit; ctx.inTemplate = checkIfInTemplate(cbody()); // Do not need to fill in other context fields such as outer and // finalVars, since the class has already undergone toplevel // flattening (which we don't want to repeat). innerUnit.flattenClasses(&ctx); innerUnit.resolvePackage( 0, 0, 0, 0 ); bool postponed = false; TreeNode::TypeContext tctx(postponed); innerUnit.buildTypeEnv( &tctx ); innerUnit.resolveTypes( &tctx ); innerUnit.loaded(!postponed); // Set the class's decl's supertype so that field resolution that // depends on it (e.g. checking assignability) works. if (dtype()->decl()->category() == Decl::Class) cbody()->decl()->superClass(static_cast(dtype()->decl())); else cbody()->decl()->interfaces(cons(static_cast(dtype()->decl()))); // Add qualifier to args. args(rewriteArgs(args(), qualifier())); // Fix the type. dtype(cbody()->decl()->asType()); // Signify that this has already undergone resolution once. _fieldResolutions = 1; // The qualified anonymous class has a reference to this; it will // call resolveField() on this later once its constructors have // been resolved. return this; } } // translate to Java 1.0 (enclosing instance is first arg to constructor) if (dtype()->decl()->source()->hasEnclosingInstance()) args(rewriteArgs(args(), qualifier())); else error() << "qualified allocation of non-inner " << dtype()->decl()->errorName() << endl; qualifier(TreeNode::omitted); } else if (dtype()->decl()->source()->hasEnclosingInstance()) { // check if this or enclosing instance can be used as qualifier int nesting = checkUnqualifiedAllocation(this, dtype(), ctx); if (nesting < 0) args(rewriteArgs(args(), new NullPntrNode(position()))); else args(rewriteArgs(args(), writeNthEncloser(ctx, position(), nesting))); } Kind dkind = dtype()->kind(); if (dkind != ClassKind && dkind != ImmutableKind) { dtype()->error() << "cannot allocate something of non-class type " << dtype()->typeName() << endl; } else if (dtype()->decl()->modifiers() & Abstract) { error() << "can't allocate abstract " << dtype()->decl()->errorName() << endl; } else { EnvironIter methods = dtype()->decl()->environ() ->lookupFirstProper (dtype()->decl()->name(), Decl::Constructor); constructor = resolveCall(methods, (Modifiers) (dtype()->modifiers() | Local), args(), position(), dtype()->decl()->source()->hasEnclosingInstance() ? false : true); checkFieldAccess(constructor, dtype()->decl(), false, false, true, ctx, position()); } decl(constructor); // Signify that this has already undergone complete resolution. _fieldResolutions = 2; return this; } TreeNode *ForEachStmtNode::resolveField(FieldContext *ctx, bool *) { TypeNode *ptype = NULL; // the appropriate point type // Fix types in foreach statement (this doesn't really belong here) foriter (var, vars()->allChildren(), TreeNode::ChildIter) { (*var)->dtypeopt((*var)->dtypeopt()->resolveField(ctx, NULL)); (*var)->initExpr((*var)->initExpr()->resolveField(ctx, NULL)); if (!ptype) // Pick an arity for the statement { TypeNode *initType = (*var)->initExpr()->type(); // non-domain types get a 0 arity if we don't know the arity yet // (i.e. we leave the type introduced by resolveName) // Note: It is important that all (typechecked) foreach // statements use the same point type because st-single.cc // relies on this type being shared to mark all variables // as single (or not) if (initType->isDomainOrRectDomainType()) { ptype = makePointType(initType->tiArity()); ptype->modifiers(Local); } } if (ptype) (*var)->simpName()->decl()->type(ptype); } stmt()->resolveField(ctx, NULL); return this; } TreeNode *ArrayAccessNode::resolveField(FieldContext *ctx, bool *inAssignment) { // Remove the implicit point creation added by the parser if // it isn't correct, i.e. if it is a Point<1> and either: // a) the array is not a Titanium array // b) the element of the point is actually a Point // This doesn't belong here with field resolution, but isn't worth a // separate pass. array(array()->resolveField(ctx, NULL)); index(index()->resolveField(ctx, NULL)); // [] overloading if (!array()->type()->isArrayType()) if (inAssignment) { // Notify AssignNode that this is an overloaded array assignment *inAssignment = true; return this; // handle in AssignNode } else return overloadArray(); if (isPointNode( index() )) { TreeNode *pointArgs = index()->args(); if (pointArgs->arity() == 1) // a Point<1> literal if (array()->type()->isJavaArrayType() || array()->type()->isPointType() || pointArgs->child(0)->type()->isPointType()) index(pointArgs->child(0)); } return this; } TreeNode *AssignNode::resolveField(FieldContext *ctx, bool *) { bool overloadedArrayAccess = false; opnd0(opnd0()->resolveField(ctx, &overloadedArrayAccess)); opnd1(opnd1()->resolveField(ctx, NULL)); if (!overloadedArrayAccess) return this; return overloadArrayAssign(); }