/* name.cc: Basic name resolution function */ #include "AST.h" #include "decls.h" #include "errors.h" #include "code-util.h" EnvironIter qualifiedLookup(TreeNode *name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, bool &postponed, int categories, bool canonical) { 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::Class | Decl::Interface) != 0) newCategories |= Decl::Class | Decl::Interface | Decl::TypePseudonym; // << added if ((categories & (Decl::Field | Decl::Method)) != 0) newCategories |= (Decl::Class | Decl::Interface | Decl::TypePseudonym | Decl::Field | Decl::LocalVar | Decl::Formal); if (!isTemplateNameNode(name->qualifier())) name->qualifier (resolveAName (name->qualifier(), env, currentClass, thisModifiers, currentPackage, postponed, newCategories, canonical)); if (!name->qualifier()->decl()) { postponed = true; name->postpone( "name lookup", "qualified access with unresolved container type" ); return possibles; } Decl* container = name->qualifier()->decl(); if (container->isTemplateBasis()) { name->error() << "cannot select " << *name->ident() << " from template basis " << container->errorName() << endl; return possibles; } // If this is a nested type name, we could be in type resolution, in which // case it's too early to build environments. bool build = !((container->category() & (Decl::Class | Decl::Interface)) && (categories & (Decl::Class | Decl::Interface)) && !(categories & ~(Decl::Class | Decl::Interface | Decl::TypePseudonym | Decl::Package))); TypeNode *type = container->hasType() ? container->type() : NULL; Decl *typeDecl = type ? type->decl() : container; // Hack to make sure container undergoes necessary resolution phases. Environ *env = (typeDecl && typeDecl->hasEnviron()) ? typeDecl->environ(build) : NULL; // Check if container is ready, unless only canonical names are allowed. if (!canonical && (!typeDecl || ((categories & (Decl::Class | Decl::Interface)) && !typeDecl->isTypeReady()) || ((categories & (Decl::Field)) && !typeDecl->isFieldReady()) || ((categories & (Decl::Method)) && !typeDecl->isMethodReady()))) { // Type env or inheritance resolution incomplete. postponed = true; name->postpone("name lookup", "qualified access with unready container"); return possibles; } if (container->hasEnviron()) { possibles = env->lookupFirstProper (name->ident(), categories); } else if (container->hasType()) { if (type->isPrimitive()) name->error() << "cannot select " << *name->ident() << " from non-reference type " << type->typeName() << endl; else { if (typeDecl) possibles = env->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 canonical) { bool postponed = false; EnvironIter result = qualifiedLookup(name, env, currentClass, thisModifiers, currentPackage, postponed, categories, canonical); assert(!postponed); return result; } // Check access privileges to a class's member. static bool checkAccess(Decl *accessor, Decl *source, Decl::Modifiers m) { extern bool isEnclosingAccess(Decl *, Decl *); return true; // TEMPORARY if (m & Common::Public) return true; else if (m & Common::Private) return (accessor == source) || isEnclosingAccess(accessor, source); else if (m & Common::Protected) return (accessor->container() == source->container()) || isSubClass(accessor, source); else return accessor->container() == source->container(); } static void findPossibles(TreeNode* name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, EnvironIter& possibles, bool &postponed, int categories, bool canonical = false) { int errs0 = NumErrors(); possibles = qualifiedLookup(name, env, currentClass, thisModifiers, currentPackage, postponed, categories, canonical); //if (currentClass && currentClass->decl()) // possibles = *disambiguate(name, possibles, currentClass->decl(), categories); if (!postponed) { Decl* d; if (possibles.isDone()) { if (errs0 == NumErrors()) { ostream &error = name->error(); error << *name->ident() << " undefined"; if (!name->qualifier()->absent()) { if (name->qualifier()->decl()->isType()) error << " in " << name->qualifier()->decl()->errorName(); else if (name->qualifier()->decl()->hasType()) error << " in " << name->qualifier()->decl()->type()->decl()->errorName(); else error << " in " << name->qualifier()->decl()->errorName(); } else 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, bool &postponed, int categories, bool canonical) { if (name->decl() != NULL) return name; EnvironIter possibles; findPossibles(name, env, currentClass, thisModifiers, currentPackage, possibles, postponed, categories, canonical); 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) && (d->category() == Decl::Field)) res = new TypeFieldAccessNode(currentClass, name); else // Static method accesses get rewritten in field resolution. res = new ThisFieldAccessNode (currentClass, thisModifiers, name); else if (name->qualifier()->decl()->category() & (Decl::Class | Decl::Interface)) { TreeNode *t = name->qualifier(); /* DOB: MAJOR HACK: rewrite (TypeNameNode (TemplateNameNode *titanium-type*)) to just *titanium-type* */ if (isTemplateNameNode(t) && t->ttype()->isTitaniumBuiltinType()) res = new TypeFieldAccessNode(t->ttype(), name); else res = new TypeFieldAccessNode(new TypeNameNode(t), 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(); // need to fix this access check if ((!d->source()->enclosingType() && !((d->modifiers() & Common::Public) || d->container() == currentPackage)) || (d->source()->enclosingType() && currentClass && !checkAccess(currentClass->decl(), d->source()->enclosingType()->decl(), d->modifiers()))) name->error() << d->errorName() << " not accessible" << endl; default: break; } return name; } TreeNode* resolveAName (TreeNode* name, Environ& env, TypeNode *currentClass, Common::Modifiers thisModifiers, Decl *currentPackage, int categories, bool canonical) { bool postponed = false; TreeNode * const result = resolveAName(name, env, currentClass, thisModifiers, currentPackage, postponed, categories, canonical); assert(!postponed); return result; } static Common::Modifiers relevantFlags(Common::Modifiers mods, bool reportLocal) { return (Common::Modifiers) (mods & ((reportLocal ? Common::Local : Common::None) | Common::NonsharedQ | Common::PolysharedQ)) ; } Decl* resolveCall(EnvironIter methods, Common::Modifiers actualFlags, TreeNode *args, SourcePosn posn, bool printFirst) { 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); if (!printFirst) { free_all(types); types = NULL; for (int i = args->arity() - 1; i > 0; i--) types = cons (static_cast(args->child(i)->type()), types); } TreeListNode * const errorTypes = printFirst ? 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) { string flags = stringifyModifiers(relevantFlags(actualFlags, false)); if (!flags.empty()) flags = "[" + flags + "] "; Error(posn) << "no matching " << (aMethod->category() == Decl::Method ? "method " : "constructor for ") << flags << aMethod->fullName() << '(' << TypeListToString(errorTypes) << ')' << endl; return UnknownMethodDecl; } d = matches->front(); bool ambiguous = false; for (llist* m = matches->tail(); m != NULL; m = m->tail()) { if (!isMoreSpecific(m->front(), d) && !isMoreSpecific(d, m->front())) { if (!(d->modifiers() & Common::Abstract) && !(m->front()->modifiers() & Common::Abstract)) ambiguous = true; else if (d->modifiers() & Common::Abstract) d = m->front(); } else if (isMoreSpecific(m->front(), d)) { d = m->front(); ambiguous = false; } } if (!ambiguous) { free_all(matches); return d; } // This really is redundant. Incompatible method overloading should // already be caught in class/inheritance resolution. // AK -- not true; see JLS 2 Section 15.12.2.2. But I have set up the above // code (mostly by accident) to not report ambiguous calls to illegally // overloaded methods. 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: " : " "; bool isMethod = (candidate.category() == Decl::Method); string flags = stringifyModifiers(relevantFlags(candidate.modifiers(), isMethod)); if (!flags.empty()) flags = "[" + flags + "] "; candidate.source()->message() << prefix << (isMethod ? "method " : "constructor for ") << flags << candidate.fullName() << endl; } d = matches->front(); free_all (matches); return d; }