/* name.cc: Basic name resolution function */ #include "AST.h" #include "decls.h" #include "errors.h" EnvironIter qualifiedLookup(TreeNode *name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, int categories, bool &postponed) { EnvironIter possibles = EnvironIter(); if (name->qualifier()->absent()) { if ((categories & (Decl::Field | Decl::Method | Decl::LocalVar | Decl::Formal | Decl::Class | Decl::Interface)) != 0) { possibles = env.lookupFirst (name->ident(), categories); } else possibles = PackageDecl::System->environ() -> lookupFirst (name->ident(), categories); } else { int newCategories = 0; if ((categories & (Decl::Class | Decl::Interface | Decl::Package)) != 0) newCategories |= Decl::Package; if ((categories & (Decl::Field | Decl::Method)) != 0) newCategories |= (Decl::Class | Decl::Interface | Decl::Field | Decl::LocalVar | Decl::Formal); name->qualifier (resolveAName (name->qualifier(), env, currentClass, thisModifiers, currentPackage, newCategories)); Decl* container = name->qualifier()->decl(); if (container->hasEnviron()) { possibles = container->environ() -> lookupFirstProper (name->ident(), categories); } else if (container->hasType()) { TypeNode* type = container->type(); if (type->isPrimitive()) name->error() << "cannot select " << *name->ident() << " from non-reference type " << type->typeName() << endl; else { Decl * const typeDecl = type->decl(); if (typeDecl) possibles = type->decl()->environ() -> lookupFirstProper (name->ident(), categories & (Decl::Field | Decl::Method)); else { postponed = true; name->postpone( "name resolution", "qualified access with unresolved container type" ); } } } } return possibles; } EnvironIter qualifiedLookup(TreeNode *name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, int categories) { bool postponed = false; EnvironIter result = qualifiedLookup(name, env, currentClass, thisModifiers, currentPackage, categories, postponed); assert(!postponed); return result; } static void findPossibles(TreeNode* name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, EnvironIter& possibles, int categories, bool &postponed) { int errs0 = NumErrors(); possibles = qualifiedLookup(name, env, currentClass, thisModifiers, currentPackage, categories, postponed); if (!postponed) { Decl* d; if (possibles.isDone()) { if (errs0 == NumErrors()) { ostream &error = name->error(); error << *name->ident() << " undefined"; if (currentClass) error << " in " << currentClass->decl()->errorName(); error << endl; } if ((categories & Decl::Method) != 0) d = UnknownMethodDecl; else if ((categories & (Decl::Field | Decl::LocalVar | Decl::Formal)) != 0) d = UnknownFieldDecl; else if (categories & (Decl::Class | Decl::Interface)) d = UnknownClassDecl; else d = UnknownPackageDecl; } else d = &*possibles; name->decl (d); } } /* * Resolve NAME (if not already resolved) with a matching Decl from * ENV whose category() is one of CATEGORIES. Fill in NAME's decl() * attribute with the first such Decl*. * * For fields/methods, returns a node for accesssing NAME---an * ObjectNode, ThisFieldAccessNode, ObjectFieldAccessNode or * StaticFieldAccessNode. * * For pseudonyms, returns the actual TreeNode to which the pseudonym * refers. * * Otherwise returns the modified NAME. */ TreeNode* resolveAName (TreeNode* name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, int categories, bool &postponed) { if (name->decl() != NULL) return name; EnvironIter possibles; findPossibles(name, env, currentClass, thisModifiers, currentPackage, possibles, categories, postponed); if (moreThanOne (possibles) && (categories & Decl::Method) == 0) name->error() << "ambiguous reference to " << *name->ident() << endl; Decl * const d = name->decl(); if (d) switch (d->category()) { case Decl::Field: case Decl::Method: { TreeNode* res; if (name->qualifier()->absent()) if (d->modifiers() & Common::Static) res = new TypeFieldAccessNode(currentClass, name); else res = new ThisFieldAccessNode (currentClass, thisModifiers, name); else if (name->qualifier()->decl()->category() & (Decl::Class | Decl::Interface)) res = new TypeFieldAccessNode(new TypeNameNode(name->qualifier()), name); else res = new ObjectFieldAccessNode(name->qualifier(), name); name->qualifier (TreeNode::omitted); return res; } case Decl::LocalVar: case Decl::Formal: return new ObjectNode (name, name->position()); case Decl::ConstPseudonym: case Decl::TypePseudonym: return static_cast< PseudonymDecl * >( d )->referent.deepClone(); case Decl::Class: case Decl::Interface: d->drequire(); if (!((d->modifiers() & Common::Public) || d->container() == currentPackage)) name->error() << d->errorName() << " not accessible" << endl; } return name; } TreeNode* resolveAName (TreeNode* name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, int categories) { bool postponed = false; TreeNode * const result = resolveAName(name, env, currentClass, thisModifiers, currentPackage, categories, postponed); assert(!postponed); return result; } Decl* resolveCall(EnvironIter methods, Common::Modifiers actualFlags, TreeNode *args, SourcePosn posn) { Decl *aMethod = &*methods; Decl* d; llist* types; types = NULL; foriter (arg, args->allChildren(), TreeNode::ChildIter) types = cons (static_cast((*arg)->type()), types); types = dreverse (types); TreeListNode * const argTypes = new TreeListNode (types); free_all (types); llist* matches; matches = NULL; foriter (method, methods, EnvironIter) { const Common::Modifiers callFlags = method->modifiers() & Common::Static ? Common::None : actualFlags; if (method->type()->isCallableWith(callFlags, argTypes)) matches = cons (&*method, matches); } if (matches == NULL) { Error(posn) << "no matching " << aMethod->errorName() << '(' << TypeListToString(argTypes) << ')' << endl; return UnknownMethodDecl; } for (llist* m0 = matches; m0 != NULL; m0 = m0->tail()) { for (llist* m1 = matches; m1 != NULL; m1 = m1->tail()) { if (m0->front() == m1->front()) continue; if (! isMoreSpecific (m0->front(), m1->front()) || isMoreSpecific (m1->front(), m0->front())) goto NotThisOne; } d = m0->front(); free_all (matches); return d; NotThisOne: ; } Error (posn) << "ambiguous method call to " << aMethod->errorName() << endl; for (llist* walk = matches; walk; walk = walk->tail()) { Decl &candidate = *walk->front(); const char *prefix = (walk == matches) ? "candidates are: " : " "; candidate.source()->message() << prefix << candidate.errorName() << endl; } d = matches->front(); free_all (matches); return d; }