diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 8ad14b1e47..a9c6ea3c25 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1519,7 +1519,7 @@ DIAG(err_anonymous_record_bad_member, ERROR, DIAG(err_anonymous_record_nonpublic_member, ERROR, "anonymous %select{struct|union}0 cannot contain a %select{private|protected}1 data member") -// Derived classes. +// C++ derived classes DIAG(err_dup_virtual, ERROR, "duplicate 'virtual' in base specifier") DIAG(err_base_clause_on_union, ERROR, @@ -1536,6 +1536,12 @@ DIAG(err_duplicate_base_class, ERROR, DIAG(err_ambiguous_derived_to_base_conv, ERROR, "ambiguous conversion from derived class %0 to base class %1:%2") +// C++ member name lookup +DIAG(err_ambiguous_member_multiple_subobjects, ERROR, + "non-static member %0 found in multiple base-class subobjects of type %1") +DIAG(err_ambiguous_member_multiple_subobject_types, ERROR, + "member %0 found in multiple base classes of different types") + // C++ operator overloading DIAG(err_operator_overload_needs_class_or_enum, ERROR, "overloaded %0 must have at least one parameter of class " diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 899cb4a209..eb718f7f1a 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -71,6 +71,7 @@ namespace clang { class ObjCContainerDecl; struct BlockSemaInfo; class BasePaths; + class MemberLookupCriteria; /// PragmaPackStack - Simple class to wrap the stack used by #pragma /// pack. @@ -596,6 +597,10 @@ public: /// field is determined by the kind of name we're searching for. unsigned IDNS; + LookupCriteria() + : Kind(Ordinary), AllowLazyBuiltinCreation(true), + RedeclarationOnly(false), IDNS(Decl::IDNS_Ordinary) { } + LookupCriteria(NameKind K, bool RedeclarationOnly, bool CPlusPlus); bool isLookupResult(Decl *D) const; @@ -620,13 +625,19 @@ public: mutable enum { /// First is a single declaration (a Decl*), which may be NULL. SingleDecl, + /// [First, Last) is an iterator range represented as opaque /// pointers used to reconstruct IdentifierResolver::iterators. OverloadedDeclFromIdResolver, + /// [First, Last) is an iterator range represented as opaque /// pointers used to reconstruct DeclContext::lookup_iterators. OverloadedDeclFromDeclContext, - /// FIXME: Cope with ambiguous name lookup. + + /// First is a pointer to a BasePaths structure, which is owned + /// by the LookupResult. Last is non-zero to indicate that the + /// ambiguity is caused by two names found in base class + /// subobjects of different types. AmbiguousLookup } StoredKind; @@ -634,15 +645,16 @@ public: /// lookup result. This may be a Decl* (if StoredKind == /// SingleDecl), the opaque pointer from an /// IdentifierResolver::iterator (if StoredKind == - /// OverloadedDeclFromIdResolver), or a - /// DeclContext::lookup_iterator (if StoredKind == - /// OverloadedDeclFromDeclContext). + /// OverloadedDeclFromIdResolver), a DeclContext::lookup_iterator + /// (if StoredKind == OverloadedDeclFromDeclContext), or a + /// BasePaths pointer (if StoredKind == AmbiguousLookup). mutable uintptr_t First; /// The last lookup result, whose contents depend on the kind of /// lookup result. This may be unused (if StoredKind == - /// SingleDecl) or it may have the same type as First (for - /// overloaded function declarations). + /// SingleDecl), it may have the same type as First (for + /// overloaded function declarations), or is may be used as a + /// Boolean value (if StoredKind == AmbiguousLookup). mutable uintptr_t Last; /// Context - The context in which we will build any @@ -655,32 +667,62 @@ public: enum LookupKind { /// @brief No entity found met the criteria. NotFound = 0, + /// @brief Name lookup found a single declaration that met the - /// criteria. + /// criteria. getAsDecl will return this declaration. Found, + /// @brief Name lookup found a set of overloaded functions that - /// met the criteria. + /// met the criteria. getAsDecl will turn this set of overloaded + /// functions into an OverloadedFunctionDecl. FoundOverloaded, - /// @brief Name lookup resulted in an ambiguity, e.g., because - /// the name was found in two different base classes. - Ambiguous + + /// Name lookup results in an ambiguity because multiple + /// entities that meet the lookup criteria were found in + /// subobjects of different types. For example: + /// @code + /// struct A { void f(int); } + /// struct B { void f(double); } + /// struct C : A, B { }; + /// void test(C c) { + /// c.f(0); // error: A::f and B::f come from subobjects of different + /// // types. overload resolution is not performed. + /// } + /// @endcode + AmbiguousBaseSubobjectTypes, + + /// Name lookup results in an ambiguity because multiple + /// nonstatic entities that meet the lookup criteria were found + /// in different subobjects of the same type. For example: + /// @code + /// struct A { int x; }; + /// struct B : A { }; + /// struct C : A { }; + /// struct D : B, C { }; + /// int test(D d) { + /// return d.x; // error: 'x' is found in two A subobjects (of B and C) + /// } + /// @endcode + AmbiguousBaseSubobjects }; + LookupResult() : StoredKind(SingleDecl), First(0), Last(0), Context(0) { } + LookupResult(ASTContext &Context, Decl *D) : StoredKind(SingleDecl), First(reinterpret_cast(D)), Last(0), Context(&Context) { } LookupResult(ASTContext &Context, - IdentifierResolver::iterator F, IdentifierResolver::iterator L) - : StoredKind(OverloadedDeclFromIdResolver), - First(F.getAsOpaqueValue()), Last(L.getAsOpaqueValue()), - Context(&Context) { } + IdentifierResolver::iterator F, IdentifierResolver::iterator L); LookupResult(ASTContext &Context, - DeclContext::lookup_iterator F, DeclContext::lookup_iterator L) - : StoredKind(OverloadedDeclFromDeclContext), - First(reinterpret_cast(F)), - Last(reinterpret_cast(L)), + DeclContext::lookup_iterator F, DeclContext::lookup_iterator L); + + LookupResult(ASTContext &Context, BasePaths *Paths, + bool DifferentSubobjectTypes) + : StoredKind(AmbiguousLookup), + First(reinterpret_cast(Paths)), + Last(DifferentSubobjectTypes? 1 : 0), Context(&Context) { } LookupKind getKind() const; @@ -688,11 +730,16 @@ public: /// @brief Determine whether name look found something. operator bool() const { return getKind() != NotFound; } + /// @brief Determines whether the lookup resulted in an ambiguity. + bool isAmbiguous() const { return StoredKind == AmbiguousLookup; } + /// @brief Allows conversion of a lookup result into a /// declaration, with the same behavior as getAsDecl. operator Decl*() const { return getAsDecl(); } Decl* getAsDecl() const; + + BasePaths *getBasePaths() const; }; LookupResult LookupName(Scope *S, DeclarationName Name, @@ -701,12 +748,15 @@ public: LookupCriteria Criteria); LookupResult LookupParsedName(Scope *S, const CXXScopeSpec &SS, DeclarationName Name, LookupCriteria Criteria); - LookupResult LookupDecl(DeclarationName Name, unsigned NSI, Scope *S, const DeclContext *LookupCtx = 0, bool enableLazyBuiltinCreation = true, bool LookInParent = true, bool NamespaceNameOnly = false); + + bool DiagnoseAmbiguousLookup(LookupResult &Result, DeclarationName Name, + SourceLocation NameLoc, + SourceRange LookupRange = SourceRange()); //@} ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *Id); @@ -1270,7 +1320,8 @@ public: bool IsDerivedFrom(QualType Derived, QualType Base); bool IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths); - + bool LookupInBases(CXXRecordDecl *Class, const MemberLookupCriteria& Criteria, + BasePaths &Paths); bool CheckDerivedToBaseConversion(QualType Derived, QualType Base, SourceLocation Loc, SourceRange Range); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 5119388dbc..468e95523c 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -39,12 +39,28 @@ Sema::TypeTy *Sema::isTypeName(IdentifierInfo &II, Scope *S, return 0; DC = static_cast(SS->getScopeRep()); } - Decl *IIDecl = LookupDecl(&II, Decl::IDNS_Ordinary, S, DC, false); + LookupResult Result = LookupDecl(&II, Decl::IDNS_Ordinary, S, DC, false); - if (IIDecl && (isa(IIDecl) || - isa(IIDecl) || - isa(IIDecl) || - isa(IIDecl))) + Decl *IIDecl = 0; + switch (Result.getKind()) { + case LookupResult::NotFound: + case LookupResult::FoundOverloaded: + case LookupResult::AmbiguousBaseSubobjectTypes: + case LookupResult::AmbiguousBaseSubobjects: + // FIXME: In the event of an ambiguous lookup, we could visit all of + // the entities found to determine whether they are all types. This + // might provide better diagnostics. + return 0; + + case LookupResult::Found: + IIDecl = Result.getAsDecl(); + break; + } + + if (isa(IIDecl) || + isa(IIDecl) || + isa(IIDecl) || + isa(IIDecl)) return IIDecl; return 0; } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d478df0bc3..33d73bf106 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -546,14 +546,22 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, } // Could be enum-constant, value decl, instance variable, etc. - Decl *D; + Decl *D = 0; + LookupResult Lookup; if (SS && !SS->isEmpty()) { DeclContext *DC = static_cast(SS->getScopeRep()); if (DC == 0) return true; - D = LookupDecl(Name, Decl::IDNS_Ordinary, S, DC); + Lookup = LookupDecl(Name, Decl::IDNS_Ordinary, S, DC); } else - D = LookupDecl(Name, Decl::IDNS_Ordinary, S); + Lookup = LookupDecl(Name, Decl::IDNS_Ordinary, S); + + if (Lookup.isAmbiguous()) + return DiagnoseAmbiguousLookup(Lookup, Name, Loc, + SS && SS->isSet()? SS->getRange() + : SourceRange()); + else + D = Lookup.getAsDecl(); // If this reference is in an Objective-C method, then ivar lookup happens as // well. @@ -1444,11 +1452,21 @@ ActOnMemberReferenceExpr(Scope *S, ExprTy *Base, SourceLocation OpLoc, // The record definition is complete, now make sure the member is valid. // FIXME: Qualified name lookup for C++ is a bit more complicated // than this. - Decl *MemberDecl = LookupDecl(DeclarationName(&Member), Decl::IDNS_Ordinary, - S, RDecl, false, false); - if (!MemberDecl) + LookupResult Result + = LookupQualifiedName(RDecl, DeclarationName(&Member), + LookupCriteria(LookupCriteria::Member, + /*RedeclarationOnly=*/false, + getLangOptions().CPlusPlus)); + + Decl *MemberDecl = 0; + if (!Result) return Diag(MemberLoc, diag::err_typecheck_no_member) << &Member << BaseExpr->getSourceRange(); + else if (Result.isAmbiguous()) + return DiagnoseAmbiguousLookup(Result, DeclarationName(&Member), + MemberLoc, BaseExpr->getSourceRange()); + else + MemberDecl = Result; if (FieldDecl *FD = dyn_cast(MemberDecl)) { // We may have found a field within an anonymous union or struct diff --git a/lib/Sema/SemaInherit.cpp b/lib/Sema/SemaInherit.cpp index a02dd1edd5..0791ac21f8 100644 --- a/lib/Sema/SemaInherit.cpp +++ b/lib/Sema/SemaInherit.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Type.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/Diagnostic.h" +#include #include #include #include @@ -45,6 +46,17 @@ void BasePaths::clear() { DetectedVirtual = 0; } +/// @brief Swaps the contents of this BasePaths structure with the +/// contents of Other. +void BasePaths::swap(BasePaths &Other) { + Paths.swap(Other.Paths); + ClassSubobjects.swap(Other.ClassSubobjects); + std::swap(FindAmbiguities, Other.FindAmbiguities); + std::swap(RecordPaths, Other.RecordPaths); + std::swap(DetectVirtual, Other.DetectVirtual); + std::swap(DetectedVirtual, Other.DetectedVirtual); +} + /// IsDerivedFrom - Determine whether the type Derived is derived from /// the type Base, ignoring qualifiers on Base and Derived. This /// routine does not assess whether an actual conversion from a @@ -65,8 +77,6 @@ bool Sema::IsDerivedFrom(QualType Derived, QualType Base) { /// ambiguous paths (if @c Paths.isFindingAmbiguities()) and record /// information about all of the paths (if @c Paths.isRecordingPaths()). bool Sema::IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths) { - bool FoundPath = false; - Derived = Context.getCanonicalType(Derived).getUnqualifiedType(); Base = Context.getCanonicalType(Base).getUnqualifiedType(); @@ -76,71 +86,112 @@ bool Sema::IsDerivedFrom(QualType Derived, QualType Base, BasePaths &Paths) { if (Derived == Base) return false; - if (const RecordType *DerivedType = Derived->getAsRecordType()) { - const CXXRecordDecl *Decl - = static_cast(DerivedType->getDecl()); - for (CXXRecordDecl::base_class_const_iterator BaseSpec = Decl->bases_begin(); - BaseSpec != Decl->bases_end(); ++BaseSpec) { - // Find the record of the base class subobjects for this type. - QualType BaseType = Context.getCanonicalType(BaseSpec->getType()); - BaseType = BaseType.getUnqualifiedType(); - - // Determine whether we need to visit this base class at all, - // updating the count of subobjects appropriately. - std::pair& Subobjects = Paths.ClassSubobjects[BaseType]; - bool VisitBase = true; - bool SetVirtual = false; - if (BaseSpec->isVirtual()) { - VisitBase = !Subobjects.first; - Subobjects.first = true; - if (Paths.isDetectingVirtual() && Paths.DetectedVirtual == 0) { - // If this is the first virtual we find, remember it. If it turns out - // there is no base path here, we'll reset it later. - Paths.DetectedVirtual = static_cast( - BaseType->getAsRecordType()); - SetVirtual = true; - } - } else - ++Subobjects.second; + return LookupInBases(cast(Derived->getAsRecordType())->getDecl(), + MemberLookupCriteria(Base), Paths); +} +/// LookupInBases - Look for something that meets the specified +/// Criteria within the base classes of Class (or any of its base +/// classes, transitively). This routine populates BasePaths with the +/// list of paths that one can take to find the entity that meets the +/// search criteria, and returns true if any such entity is found. The +/// various options passed to the BasePath constructor will affect the +/// behavior of this lookup, e.g., whether it finds ambiguities, +/// records paths, or attempts to detect the use of virtual base +/// classes. +bool Sema::LookupInBases(CXXRecordDecl *Class, + const MemberLookupCriteria& Criteria, + BasePaths &Paths) { + bool FoundPath = false; + + for (CXXRecordDecl::base_class_const_iterator BaseSpec = Class->bases_begin(), + BaseSpecEnd = Class->bases_end(); + BaseSpec != BaseSpecEnd; ++BaseSpec) { + // Find the record of the base class subobjects for this type. + QualType BaseType = Context.getCanonicalType(BaseSpec->getType()); + BaseType = BaseType.getUnqualifiedType(); + + // Determine whether we need to visit this base class at all, + // updating the count of subobjects appropriately. + std::pair& Subobjects = Paths.ClassSubobjects[BaseType]; + bool VisitBase = true; + bool SetVirtual = false; + if (BaseSpec->isVirtual()) { + VisitBase = !Subobjects.first; + Subobjects.first = true; + if (Paths.isDetectingVirtual() && Paths.DetectedVirtual == 0) { + // If this is the first virtual we find, remember it. If it turns out + // there is no base path here, we'll reset it later. + Paths.DetectedVirtual = cast(BaseType->getAsRecordType()); + SetVirtual = true; + } + } else + ++Subobjects.second; + + if (Paths.isRecordingPaths()) { + // Add this base specifier to the current path. + BasePathElement Element; + Element.Base = &*BaseSpec; + if (BaseSpec->isVirtual()) + Element.SubobjectNumber = 0; + else + Element.SubobjectNumber = Subobjects.second; + Paths.ScratchPath.push_back(Element); + } + + CXXRecordDecl *BaseRecord + = cast(BaseSpec->getType()->getAsRecordType()->getDecl()); + + // Either look at the base class type or look into the base class + // type to see if we've found a member that meets the search + // criteria. + bool FoundPathToThisBase = false; + if (Criteria.LookupBase) { + FoundPathToThisBase + = (Context.getCanonicalType(BaseSpec->getType()) == Criteria.Base); + } else { + Paths.ScratchPath.Decls = BaseRecord->lookup(Criteria.Name); + while (Paths.ScratchPath.Decls.first != Paths.ScratchPath.Decls.second) { + if (Criteria.Criteria.isLookupResult(*Paths.ScratchPath.Decls.first)) { + FoundPathToThisBase = true; + break; + } + ++Paths.ScratchPath.Decls.first; + } + } + + if (FoundPathToThisBase) { + // We've found a path that terminates that this base. + FoundPath = true; if (Paths.isRecordingPaths()) { - // Add this base specifier to the current path. - BasePathElement Element; - Element.Base = &*BaseSpec; - if (BaseSpec->isVirtual()) - Element.SubobjectNumber = 0; - else - Element.SubobjectNumber = Subobjects.second; - Paths.ScratchPath.push_back(Element); + // We have a path. Make a copy of it before moving on. + Paths.Paths.push_back(Paths.ScratchPath); + } else if (!Paths.isFindingAmbiguities()) { + // We found a path and we don't care about ambiguities; + // return immediately. + return FoundPath; } + } + // C++ [class.member.lookup]p2: + // A member name f in one sub-object B hides a member name f in + // a sub-object A if A is a base class sub-object of B. Any + // declarations that are so hidden are eliminated from + // consideration. + else if (VisitBase && LookupInBases(BaseRecord, Criteria, Paths)) { + // There is a path to a base class that meets the criteria. If we're not + // collecting paths or finding ambiguities, we're done. + FoundPath = true; + if (!Paths.isFindingAmbiguities()) + return FoundPath; + } - if (Context.getCanonicalType(BaseSpec->getType()) == Base) { - // We've found the base we're looking for. - FoundPath = true; - if (Paths.isRecordingPaths()) { - // We have a path. Make a copy of it before moving on. - Paths.Paths.push_back(Paths.ScratchPath); - } else if (!Paths.isFindingAmbiguities()) { - // We found a path and we don't care about ambiguities; - // return immediately. - return FoundPath; - } - } else if (VisitBase && IsDerivedFrom(BaseSpec->getType(), Base, Paths)) { - // There is a path to the base we want. If we're not - // collecting paths or finding ambiguities, we're done. - FoundPath = true; - if (!Paths.isFindingAmbiguities()) - return FoundPath; - } - - // Pop this base specifier off the current path (if we're - // collecting paths). - if (Paths.isRecordingPaths()) - Paths.ScratchPath.pop_back(); - // If we set a virtual earlier, and this isn't a path, forget it again. - if (SetVirtual && !FoundPath) { - Paths.DetectedVirtual = 0; - } + // Pop this base specifier off the current path (if we're + // collecting paths). + if (Paths.isRecordingPaths()) + Paths.ScratchPath.pop_back(); + // If we set a virtual earlier, and this isn't a path, forget it again. + if (SetVirtual && !FoundPath) { + Paths.DetectedVirtual = 0; } } @@ -208,4 +259,3 @@ Sema::CheckDerivedToBaseConversion(QualType Derived, QualType Base, << Derived << Base << PathDisplayStr << Range; return true; } - diff --git a/lib/Sema/SemaInherit.h b/lib/Sema/SemaInherit.h index eccd31f6a6..db7bfb8e65 100644 --- a/lib/Sema/SemaInherit.h +++ b/lib/Sema/SemaInherit.h @@ -16,6 +16,8 @@ #ifndef LLVM_CLANG_SEMA_INHERIT_H #define LLVM_CLANG_SEMA_INHERIT_H +#include "clang/AST/DeclarationName.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/Type.h" #include "clang/AST/TypeOrdering.h" #include "llvm/ADT/SmallVector.h" @@ -48,12 +50,17 @@ namespace clang { /// BasePath - Represents a path from a specific derived class /// (which is not represented as part of the path) to a particular - /// (direct or indirect) base class subobject. Individual elements + /// (direct or indirect) base class subobject that contains some + /// number of declarations with the same name. Individual elements /// in the path are described by the BasePathElement structure, /// which captures both the link from a derived class to one of its /// direct bases and identification describing which base class - /// subobject is being used. - typedef llvm::SmallVector BasePath; + /// subobject is being used. + struct BasePath : public llvm::SmallVector { + /// Decls - The set of declarations found inside this base class + /// subobject. + DeclContext::lookup_result Decls; + }; /// BasePaths - Represents the set of paths from a derived class to /// one of its (direct or indirect) bases. For example, given the @@ -137,6 +144,9 @@ namespace clang { paths_iterator begin() const { return Paths.begin(); } paths_iterator end() const { return Paths.end(); } + BasePath& front() { return Paths.front(); } + const BasePath& front() const { return Paths.front(); } + bool isAmbiguous(QualType BaseType); /// isFindingAmbiguities - Whether we are finding multiple paths @@ -159,6 +169,41 @@ namespace clang { } void clear(); + + void swap(BasePaths &Other); + }; + + /// MemberLookupCriteria - Criteria for performing lookup of a + /// member of a C++ class. Objects of this type are used to direct + /// Sema::LookupCXXClassMember. + struct MemberLookupCriteria { + /// MemberLookupCriteria - Constructs member lookup criteria to + /// search for a base class of type Base. + explicit MemberLookupCriteria(QualType Base) + : LookupBase(true), Base(Base) { } + + /// MemberLookupCriteria - Constructs member lookup criteria to + /// search for a class member with the given Name. + explicit MemberLookupCriteria(DeclarationName Name, + Sema::LookupCriteria Criteria) + : LookupBase(false), Name(Name), Criteria(Criteria) { } + + /// LookupBase - True if we are looking for a base class (whose + /// type is Base). If false, we are looking for a named member of + /// the class (with the name Name). + bool LookupBase; + + /// Base - The type of the base class we're searching for, if + /// LookupBase is true. + QualType Base; + + /// Name - The name of the member we're searching for, if + /// LookupBase is false. + DeclarationName Name; + + /// Criteria - The criteria by which we evaluate a named member, + /// if LookupBase is false. + Sema::LookupCriteria Criteria; }; } diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index a9ec3f2faf..b85824382b 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// #include "Sema.h" +#include "SemaInherit.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -125,6 +127,46 @@ bool Sema::LookupCriteria::isLookupResult(Decl *D) const { return false; } +Sema::LookupResult::LookupResult(ASTContext &Context, + IdentifierResolver::iterator F, + IdentifierResolver::iterator L) + : Context(&Context) { + if (F != L && isa(*F)) { + IdentifierResolver::iterator Next = F; + ++Next; + if (Next != L && isa(*Next)) { + StoredKind = OverloadedDeclFromIdResolver; + First = F.getAsOpaqueValue(); + Last = L.getAsOpaqueValue(); + return; + } + } + + StoredKind = SingleDecl; + First = reinterpret_cast(*F); + Last = 0; +} + +Sema::LookupResult::LookupResult(ASTContext &Context, + DeclContext::lookup_iterator F, + DeclContext::lookup_iterator L) + : Context(&Context) { + if (F != L && isa(*F)) { + DeclContext::lookup_iterator Next = F; + ++Next; + if (Next != L && isa(*Next)) { + StoredKind = OverloadedDeclFromDeclContext; + First = reinterpret_cast(F); + Last = reinterpret_cast(L); + return; + } + } + + StoredKind = SingleDecl; + First = reinterpret_cast(*F); + Last = 0; +} + /// @brief Determine the result of name lookup. Sema::LookupResult::LookupKind Sema::LookupResult::getKind() const { switch (StoredKind) { @@ -136,11 +178,11 @@ Sema::LookupResult::LookupKind Sema::LookupResult::getKind() const { return FoundOverloaded; case AmbiguousLookup: - return Ambiguous; + return Last? AmbiguousBaseSubobjectTypes : AmbiguousBaseSubobjects; } - // We can't get here, but GCC complains nonetheless. - return Ambiguous; + // We can't ever get here. + return NotFound; } /// @brief Converts the result of name lookup into a single (possible @@ -180,6 +222,14 @@ Decl *Sema::LookupResult::getAsDecl() const { return 0; } +/// @brief Retrieves the BasePaths structure describing an ambiguous +/// name lookup. +BasePaths *Sema::LookupResult::getBasePaths() const { + assert((StoredKind == AmbiguousLookup) && + "getBasePaths can only be used on an ambiguous lookup"); + return reinterpret_cast(First); +} + /// @brief Perform unqualified name lookup starting from a given /// scope. /// @@ -379,13 +429,99 @@ Sema::LookupQualifiedName(DeclContext *LookupCtx, DeclarationName Name, Criteria.IDNS |= Decl::IDNS_Member; // Perform qualified name lookup into the LookupCtx. - // FIXME: Will need to look into base classes and such. DeclContext::lookup_iterator I, E; for (llvm::tie(I, E) = LookupCtx->lookup(Name); I != E; ++I) if (Criteria.isLookupResult(*I)) return LookupResult(Context, I, E); - return LookupResult(Context, 0); + // If this isn't a C++ class or we aren't allowed to look into base + // classes, we're done. + if (Criteria.RedeclarationOnly || !isa(LookupCtx)) + return LookupResult(Context, 0); + + // Perform lookup into our base classes. + BasePaths Paths; + + // Look for this member in our base classes + if (!LookupInBases(cast(LookupCtx), + MemberLookupCriteria(Name, Criteria), Paths)) + return LookupResult(Context, 0); + + // C++ [class.member.lookup]p2: + // [...] If the resulting set of declarations are not all from + // sub-objects of the same type, or the set has a nonstatic member + // and includes members from distinct sub-objects, there is an + // ambiguity and the program is ill-formed. Otherwise that set is + // the result of the lookup. + // FIXME: support using declarations! + QualType SubobjectType; + int SubobjectNumber; + for (BasePaths::paths_iterator Path = Paths.begin(), PathEnd = Paths.end(); + Path != PathEnd; ++Path) { + const BasePathElement &PathElement = Path->back(); + + // Determine whether we're looking at a distinct sub-object or not. + if (SubobjectType.isNull()) { + // This is the first subobject we've looked at. Record it's type. + SubobjectType = Context.getCanonicalType(PathElement.Base->getType()); + SubobjectNumber = PathElement.SubobjectNumber; + } else if (SubobjectType + != Context.getCanonicalType(PathElement.Base->getType())) { + // We found members of the given name in two subobjects of + // different types. This lookup is ambiguous. + BasePaths *PathsOnHeap = new BasePaths; + PathsOnHeap->swap(Paths); + return LookupResult(Context, PathsOnHeap, true); + } else if (SubobjectNumber != PathElement.SubobjectNumber) { + // We have a different subobject of the same type. + + // C++ [class.member.lookup]p5: + // A static member, a nested type or an enumerator defined in + // a base class T can unambiguously be found even if an object + // has more than one base class subobject of type T. + ScopedDecl *FirstDecl = *Path->Decls.first; + if (isa(FirstDecl) || + isa(FirstDecl) || + isa(FirstDecl)) + continue; + + if (isa(FirstDecl)) { + // Determine whether all of the methods are static. + bool AllMethodsAreStatic = true; + for (DeclContext::lookup_iterator Func = Path->Decls.first; + Func != Path->Decls.second; ++Func) { + if (!isa(*Func)) { + assert(isa(*Func) && "Non-function must be a tag decl"); + break; + } + + if (!cast(*Func)->isStatic()) { + AllMethodsAreStatic = false; + break; + } + } + + if (AllMethodsAreStatic) + continue; + } + + // We have found a nonstatic member name in multiple, distinct + // subobjects. Name lookup is ambiguous. + BasePaths *PathsOnHeap = new BasePaths; + PathsOnHeap->swap(Paths); + return LookupResult(Context, PathsOnHeap, false); + } + } + + // Lookup in a base class succeeded; return these results. + + // If we found a function declaration, return an overload set. + if (isa(*Paths.front().Decls.first)) + return LookupResult(Context, + Paths.front().Decls.first, Paths.front().Decls.second); + + // We found a non-function declaration; return a single declaration. + return LookupResult(Context, *Paths.front().Decls.first); } /// @brief Performs name lookup for a name that was parsed in the @@ -419,4 +555,41 @@ Sema::LookupParsedName(Scope *S, const CXXScopeSpec &SS, return LookupName(S, Name, Criteria); } +/// @brief Produce a diagnostic describing the ambiguity that resulted +/// from name lookup. +/// +/// @param Result The ambiguous name lookup result. +/// +/// @param Name The name of the entity that name lookup was +/// searching for. +/// +/// @param NameLoc The location of the name within the source code. +/// +/// @param LookupRange A source range that provides more +/// source-location information concerning the lookup itself. For +/// example, this range might highlight a nested-name-specifier that +/// precedes the name. +/// +/// @returns true +bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result, DeclarationName Name, + SourceLocation NameLoc, + SourceRange LookupRange) { + assert(Result.isAmbiguous() && "Lookup result must be ambiguous"); + + if (Result.getKind() == LookupResult::AmbiguousBaseSubobjects) { + BasePaths *Paths = Result.getBasePaths(); + QualType SubobjectType = Paths->front().back().Base->getType(); + return Diag(NameLoc, diag::err_ambiguous_member_multiple_subobjects) + << Name << SubobjectType << LookupRange; + } + + assert(Result.getKind() == LookupResult::AmbiguousBaseSubobjectTypes && + "Unhandled form of name lookup ambiguity"); + + Diag(NameLoc, diag::err_ambiguous_member_multiple_subobject_types) + << Name << LookupRange; + + // FIXME: point out the members we found using notes. + return true; +}