/* Static analysis, phase 1: build the package environment. Also resolve array allocation ambiguity and rewrite AllocateArrayNodes to eliminate EmptyArrayNodes, ExpressionArrayNodes and TitaniumArrayNodes */ #include "AST.h" #include "TypeContext.h" #include "code-util.h" #include "compiler.h" #include "decls.h" #include "errors.h" #include "templates/Instantiations.h" #include "utils.h" void TreeNode::resolvePackage(Decl *package, Environ *fileEnv, Environ *enclEnviron, bool recurse) { if (recurse) { const int count = arity(); for (int sweep = 0; sweep < count; ++sweep) child(sweep)->resolvePackage(package, fileEnv, enclEnviron, true); } } void CompileUnitNode::resolvePackage(Decl *, Environ *, Environ *, bool) { foriter (type, types()->allChildren(), ChildIter) (*type)->resolvePackage (thePackage, environ(), NULL, true); // Must do this after the types, because we want to build this files environment // before we start loading the imported types foriter (import, imports()->allChildren(), ChildIter) (*import)->resolveImports(this, environ()); } // A member type have the same name as an enclosing type. static void checkNameConflict(TypeDeclNode *inner) { TypeDeclNode *outer = inner->enclosingType(); do { if (*inner->declaredName()->ident() == *outer->declaredName()->ident()) inner->error() << "type name " << *inner->declaredName()->ident() << " conflicts with name of enclosing type" << endl; } while ((outer = outer->enclosingType())); } void ClassDeclNode::resolvePackage(Decl *package, Environ *fileEnv, Environ *, bool recurse) { if (simpName()->decl()) { // This can happen for a template instantiation. Even though nested classes are // in a separate compile unit, we still need to recurse over this unit to set // up block environments. } else if (!enclosingType()) { Decl *other = fileEnv->lookupProper(simpName()->ident()); if (other) { error() << "attempt to redefine " << other->errorName() << " as a class" << endl; return; } Environ *packageEnv = fileEnv->parent(); other = packageEnv->lookupProper(simpName()->ident()); if (other && (!other->source() || other->source()->absent())) { // Assume this is the definition of 'other' other->source(this); other->modifiers(flags()); } else { ClassDecl *cl = new ClassDecl(simpName()->ident(), package, flags(), this); if (other) // Redefinition in same package. error() << "class name " << *simpName()->ident() << " conflicts with " << other->errorName() << " in same package" << endl; else packageEnv->add(cl); other = cl; } Environ *env = new Environ(fileEnv); other->environ(env); fileEnv->add(other); simpName()->decl(other); } else if (flags() & LocalClass) { Environ *enclenv = enclosingBlock()->absent() ? enclosingType()->decl()->environ(false) : enclosingBlock()->environ(); // Enclosing block can be absent if this is an anonymous class declared in // a field initializer. Decl *other = enclenv->lookup(declaredName()->ident()); if (other && (other->modifiers() & LocalClass) && (other->source()->enclosingType() == enclosingType())) { // Last check above is to ensure legal shadowing of local classes is // allowed. error() << "attempt to redefine " << other->errorName() << " as a class" << endl; return; } ClassDecl *cl = new ClassDecl(simpName()->ident(), declaredName()->ident(), package, flags(), this); Environ *env = new Environ(enclenv); simpName()->decl(cl); cl->environ(env); enclenv->add(declaredName()->ident(), cl); declaredName()->decl(cl); cl->environ(env); checkNameConflict(this); } else { Decl *other = enclosingType()->decl()->environ(false)->lookupProper(declaredName()->ident()); if (other) { error() << "attempt to redefine " << other->errorName() << " as a class" << endl; return; } ClassDecl *cl = new ClassDecl(simpName()->ident(), declaredName()->ident(), package, flags(), this); Environ *env = new Environ(enclosingType()->decl()->environ(false)); simpName()->decl(cl); cl->environ(env); enclosingType()->decl()->environ(false)->add(declaredName()->ident(), cl); declaredName()->decl(cl); cl->environ(env); checkNameConflict(this); } if (recurse) TreeNode::resolvePackage(package, fileEnv, decl()->environ(false), true); } void InterfaceDeclNode::resolvePackage(Decl *package, Environ *fileEnv, Environ *enclEnviron, bool recurse) { if (simpName()->decl()) { // This can happen for a template instantiation. Even though nested classes are // in a separate compile unit, we still need to recurse over this unit to set // up block environments. } else if (!enclosingType()) { Decl *other = fileEnv->lookupProper(simpName()->ident()); if (other) { error() << "attempt to redefine " << other->errorName() << " as an interface" << endl; return; } Environ *packageEnv = fileEnv->parent(); other = packageEnv->lookupProper(simpName()->ident()); if (other && (!other->source() || other->source()->absent())) { // Assume this is the definition of 'other' other->source(this); other->modifiers((Common::Modifiers)(Common::Interface | flags())); } else { ClassDecl *cl = new ClassDecl(simpName()->ident(), package, (Common::Modifiers) (Common::Interface | flags()), this); if (other) // Redefinition in same package. error() << "interface name " << *simpName()->ident() << " conflicts with " << other->errorName() << " in same package" << endl; else packageEnv->add(cl); other = cl; } Environ *env = new Environ(fileEnv); other->environ(env); fileEnv->add(other); simpName()->decl(other); } else { Decl *other = enclosingType()->decl()->environ(false)->lookupProper(declaredName()->ident()); if (other) { error() << "attempt to redefine " << other->errorName() << " as an interface" << endl; return; } ClassDecl *cl = new ClassDecl(simpName()->ident(), declaredName()->ident(), package, (Common::Modifiers) (Common::Interface | flags()), this); Environ *env = new Environ(enclosingType()->decl()->environ(false)); simpName()->decl(cl); cl->environ(env); enclosingType()->decl()->environ(false)->add(declaredName()->ident(), cl); declaredName()->decl(cl); cl->environ(env); checkNameConflict(this); } if (recurse) TreeNode::resolvePackage(package, fileEnv, decl()->environ(false), true); } void TemplateDeclNode::resolvePackage( Decl *package, Environ *fileEnv, Environ *, bool ) { basis()->resolvePackage( package, fileEnv, NULL, false ); // Motivation for extra resolvePackage() argument is here; it prevents the // basis from running package resolution on its members until it gets // instantiated. decl()->instantiations = new Instantiations; } void BlockNode::resolvePackage(Decl *package, Environ *fileEnv, Environ *enclEnviron, bool recurse) { environ(new Environ(enclEnviron)); if (recurse) TreeNode::resolvePackage(package, fileEnv, environ(), true); } //////////////////////////////////////////////////////////////////////// void TreeNode::resolveImports(CompileUnitNode *, Environ *) { undefined("resolveImports"); } void ImportNode::resolveImports(CompileUnitNode *, Environ *fileEnv) { int errs0 = NumErrors(); resolveAName(name(), *(PackageDecl::System->environ()), NULL, None, NULL, Decl::Class | Decl::Interface, true); if (errs0 == NumErrors()) { Decl *old = fileEnv->lookupProper(name()->ident()); if (old != NULL && old != name()->decl()) { error() << "attempt to import conflicting name: " << old->errorName() << endl; } else if (old == NULL) fileEnv->add(name()->ident(), name()->decl()); } } void ImportOnDemandNode::resolveImports(CompileUnitNode *file, Environ *) { int errs0 = NumErrors(); resolveAName(name(), *(PackageDecl::System->environ()), NULL, None, NULL, Decl::Package | Decl::Class | Decl::Interface, true); if (errs0 == NumErrors()) file->importOnDemand(name()->decl()); } void CompileUnitNode::importOnDemand(Decl *importedPackage) { // ignore duplicate imports if (find(importedPackage, importedPackages)) return; importedPackages = cons(importedPackage, importedPackages); Environ* env = environ()->parent()->parent(); foriter (type, importedPackage->environ(false)->allProperDecls(), EnvironIter) if (type->category() == Decl::Class || type->category() == Decl::Interface) env->add(type->declaredName(), &*type); // conflicts appear on use only } void CompileUnitNode::importOnDemand(const string *s1, const string *s2) { NameNode outer( TreeNode::omitted, s1, NULL ); NameNode name( &outer, s2, NULL ); int errs0 = NumErrors(); resolveAName(&name, *(PackageDecl::System->environ()), NULL, None, NULL, Decl::Package); if (errs0 == NumErrors()) importOnDemand(name.decl()); } //////////////////////////////////////////////////////////////////////// // Load all the explicitly visible types (more might appear when we // start resolving names, but we don't need those to build the class-level // environment TreeNode *TreeNode::resolveTypes(TypeContext *ctx) { const int count = arity(); for (int sweep = 0; sweep < count; ++sweep) child( sweep, child( sweep )->resolveTypes( ctx ) ); return this; } TreeNode *TypeNameNode::resolveTypes(TypeContext *ctx) { if (decl()) return this; TreeNode *qual = name(); while (!isTemplateNameNode(qual) && !qual->qualifier()->absent()) qual = qual->qualifier(); if (isTemplateNameNode(qual) && !qual->decl()) { // This type name corresponds to a type nested in a template. qual->resolveTypes(ctx); // The template may not be instantiable yet, so defer resolution until name resolution. return this; } TreeNode * const myName = name(); bool postponed = false; TreeNode * const resolved = resolveAName(myName, *ctx->typeEnv, NULL, None, ctx->package, postponed, Decl::Class | Decl::Interface | Decl::TypePseudonym); if (postponed) { ctx->postponed = true; return this; } if (myName->decl()->category() & Decl::TypePseudonym) { assert( resolved->isTypeNode() ); TypeNode &referent = static_cast< TypeNode & >( *resolved ); return referent.addModifiers( modifiers() ); // : should we be checking that the modifiers actually make sense? // : for example, what do we do with "int local" ? } else if (isTemplateDeclNode(myName->decl()->source()->parent()) && !isTemplateInstanceTypeNode(parent())) { error() << "Attempted to reference the template " << *(myName->ident()) << " outside of a template instantiation." << endl; name()->decl(UnknownClassDecl); return this; } else { assert( resolved == name() ); return this; } } // Rewrite argument list for allocation of local class. llist *rebuildArgsList(TreeListNode *tln, llist *lst) { llist *tmp = appendTreeList(tln, NULL); llist *tmp2 = NULL; foriter (p, elements(lst), ListIterator) { TreeNode *name = (*p)->simpName()->deepClone(); // PR709 -- Remove decl since it may be a FieldDecl, if local class // has already undergone class resolution. name->decl(NULL); tmp2 = cons(static_cast(new ObjectNode(name, name->position())), tmp2); } tmp2 = dreverse(tmp2); return extend(tmp2, tmp); } TreeNode *AllocateNode::resolveTypes(TypeContext *ctx) { region(region()->resolveTypes(ctx)); if (!qualifier()->absent()) qualifier(qualifier()->resolveTypes(ctx)); else if (!dtype()->decl()) { dtype((TypeNode *) dtype()->resolveTypes(ctx)); } if ((dtype()->decl() && dtype()->decl()->modifiers() & LocalClass) || !cbody()->absent()) { // Rewrite allocation arguments to pass values of finals to anon class. // We use cdn->finalVars() so as to take into account finals overridden // by fields. This works for qualified anonymous classes as well. TreeListNode *finals = cbody()->absent() ? static_cast(dtype()->decl()->source()->finalVars()) : static_cast(cbody()->finalVars()); llist *lst = appendTreeList(finals, NULL); lst = rebuildArgsList(args(), lst); args(new TreeListNode(lst, args()->position())); } args((TreeListNode *) args()->resolveTypes(ctx)); return this; } TreeNode *AllocateArrayNode::resolveTypes(TypeContext *ctx) { /* Finish parsing (due to ambiguity in new X[3][(n)d] whose meaning depends on whether n is a type */ TypeNode *dt = dtype(); llist *dims = NULL; bool exprOnly = false; for (int i = dimExprs()->arity(); i > 0; ) exprOnly = dimExprs()->child(--i)->resolveNewArray(ctx->package, ctx->fileEnv, exprOnly, &dims, &dt); if (!dims && initExpr()->absent()) error() << "No array size specified" << endl; else if (dims && !initExpr()->absent()) { error() << "Cannot specify size for anonymous array" << endl; } else if (!initExpr()->absent()) { // AK - from what I can tell, normal array allocations use the element type // as dtype() and then locally qualified dimExprs(); anonymous allocations // use the element type and the dimensions as dtype(), with no dimExprs(), // so dtype() must be manually locally qualified dt->modifiers((Modifiers) (dt->modifiers() | Local)); } dimExprs(new TreeListNode(dims)); dtype(dt); return TreeNode::resolveTypes(ctx); } bool TreeNode::resolveNewArray(Decl *, Environ *, bool, llist **, TypeNode **) { unimplemented("resolveNewArray"); return false; } bool AllocateArrayDimensionNode::resolveNewArray(Decl *, Environ *, bool exprOnly, llist **dims, TypeNode **base) { *dims = cons((TreeNode *)this, *dims); return true; } static bool arrayTypeSpecifier(TreeNode *x, bool exprOnly, TypeNode **base) { if (exprOnly) x->error() << "new: array type specifier followed by expression" << endl; *base = x->asType2(*base); return false; } bool EmptyArrayNode::resolveNewArray(Decl *, Environ *, bool exprOnly, llist **dims, TypeNode **base) { return arrayTypeSpecifier(this, exprOnly, base); } bool TitaniumArrayNode::resolveNewArray(Decl *, Environ *, bool exprOnly, llist **dims, TypeNode **base) { return arrayTypeSpecifier(this, exprOnly, base); } bool ExpressionArrayNode::resolveNewArray(Decl *package, Environ *fileEnv, bool exprOnly, llist **dims, TypeNode **base) { if (expr()->args()->arity() != 1) { error() << "Unexpected expression list" << endl; return true; } else if (expr()->isArraySpecifier(package, fileEnv)) // We get some redundant computation here, but makes life simpler return arrayTypeSpecifier(this, exprOnly, base); else { *dims = cons((TreeNode *)(new AllocateArrayDimensionNode(expr()->args()->child(0), flags())), *dims); return true; } } bool TreeNode::isArraySpecifier(Decl *, Environ *) { return false; } bool PointNode::isArraySpecifier(Decl *package, Environ *fileEnv) { if (args()->arity() != 1) return false; else return args()->child(0)->isArraySpecifier(package, fileEnv); } bool PrimitiveLitNode::isArraySpecifier(Decl *, Environ *) { return literal().isNd(); } bool CastNode::isArraySpecifier(Decl *package, Environ *fileEnv) { TypeNode *dt = dtype(); if (opnd0()->isd() && dt->isNamedType() && !dt->modifiers()) { EnvironIter typeMatches = qualifiedLookup(dt->name(), *fileEnv, NULL, None, package, Decl::Class | Decl::Interface); /* We assume it was (Expr) d if there are no matches for the type */ if (typeMatches.isDone()) return true; } return false; } TreeNode *CompileUnitNode::resolveTypes(TypeContext *ctx) { //buildTypeEnv(NULL, NULL, NULL); TypeContext subCtx(*ctx); subCtx.package = thePackage; subCtx.fileEnv = subCtx.typeEnv = environ(); types()->resolveTypes( &subCtx ); return this; } // Has this class undergone type resolution? bool ClassDecl::typesResolved() const { return mark >= 3; } TreeNode *ClassDeclNode::resolveTypes(TypeContext *ctx) { if (!decl()->isTypeReady()) { // Not ready to resolve types. ctx->postponed = true; return this; } else if (decl()->typesResolved()) { // Already resolved types. return this; } // Would not work for templated inner classes. TypeContext subCtx(*ctx); subCtx.cclass = decl()->asType(); // Supertypes have already been resolved. simpName(simpName()->resolveTypes(&subCtx)); // Resolve members in this class's environment. subCtx.typeEnv = decl()->environ(false); members((TreeListNode *) members()->resolveTypes(&subCtx)); // Mark decl as having types resolved. decl()->mark = 3; return this; } TreeNode *InterfaceDeclNode::resolveTypes(TypeContext *ctx) { if (!decl()->isTypeReady()) { // Not ready to resolve types. ctx->postponed = true; return this; } else if (decl()->typesResolved()) { // Already resolved types. return this; } TypeContext subCtx(*ctx); subCtx.cclass = decl()->asType(); // Supertypes have already been resolved. simpName(simpName()->resolveTypes(&subCtx)); // Resolve members in this interface's environment. subCtx.typeEnv = decl()->environ(false); members((TreeListNode *) members()->resolveTypes(&subCtx)); // Mark decl as having types resolved. decl()->mark = 3; return this; } TreeNode *TemplateDeclNode::resolveTypes( TypeContext *ctx ) { // : should each formal be in scope while resolving subsequent formals? params()->resolveTypes( ctx ); return this; } TreeNode *TemplateInstanceDeclNode::resolveTypes( TypeContext *ctx ) { TypeContext subCtx(*ctx); subCtx.typeEnv = environ(); return TreeNode::resolveTypes( &subCtx ); } TreeNode *TemplateInstanceTypeNode::resolveTypes( TypeContext *ctx ) { if (decl()) return this; TypeNameNode &type = static_cast< TypeNameNode & >( *dtype() ); assert( isTypeNameNode( &type ) ); // figure out what template we are using type.resolveTypes( ctx ); // template name may be used as a template within its own definition const ClassDecl &basis = *type.decl(); if (basis.templateBasis) type.name()->decl( basis.templateBasis ); // perform basic sanity checking; if everything looks sane, then // disambiguate actuals with respect to the underlying template decl( checkTemplateUse() ); // finish up type resolution for actuals args()->resolveTypes( ctx ); return this; } TreeNode *BlockNode::resolveTypes(TypeContext *ctx) { TypeContext subCtx(*ctx); subCtx.typeEnv = environ() ? environ() : ctx->typeEnv; TreeNode::resolveTypes(&subCtx); return this; } void TreeNode::packageResolution() { undefined("packageResolution"); } void CompileUnitNode::packageResolution() { if (package()->absent()) thePackage = unnamedPackage; else thePackage = resolveAName(package(), *PackageDecl::System->environ(), NULL, None, NULL, Decl::Package)->decl(); // build environment for this file Environ *importOnDemandEnv = new Environ(PackageDecl::System->environ()); Environ *packageEnv = new Environ(importOnDemandEnv); environ(new Environ(packageEnv)); // the file level environment packageEnv->copy(thePackage->environ()); importOnDemand(JavaString, LangString); importOnDemand(TitaniumString, LangString); resolvePackage(NULL, NULL, NULL, true); //resolveTypes(NULL, NULL, NULL); }