diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ee1155ff2065..3b4af8e9b5e1 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1996,6 +1996,10 @@ def err_ambiguous_member_multiple_subobject_types : Error< def note_ambiguous_member_found : Note<"member found by ambiguous name lookup">; def err_ambiguous_reference : Error<"reference to %0 is ambiguous">; def note_ambiguous_candidate : Note<"candidate found by name lookup is %q0">; +def err_ambiguous_tag_hiding : Error<"a type named %0 is hidden by a " + "declaration in a different namespace">; +def note_hidden_tag : Note<"type declaration hidden">; +def note_hiding_object : Note<"declaration hides type">; // C++ operator overloading def err_operator_overload_needs_class_or_enum : Error< diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 6c9466f86396..9d35f3dd3f57 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1074,6 +1074,13 @@ public: /// functions into an OverloadedFunctionDecl. FoundOverloaded, + /// @brief Name lookup results in an ambiguity; use + /// getAmbiguityKind to figure out what kind of ambiguity + /// we have. + Ambiguous + }; + + enum AmbiguityKind { /// Name lookup results in an ambiguity because multiple /// entities that meet the lookup criteria were found in /// subobjects of different types. For example: @@ -1117,7 +1124,22 @@ public: /// @endcode AmbiguousReference, - FirstAmbiguous = AmbiguousBaseSubobjectTypes + /// Name lookup results in an ambiguity because an entity with a + /// tag name was hidden by an entity with an ordinary name from + /// a different context. + /// @code + /// namespace A { struct Foo {}; } + /// namespace B { void Foo(); } + /// namespace C { + /// using namespace A; + /// using namespace B; + /// } + /// void test() { + /// C::Foo(); // error: tag 'A::Foo' is hidden by an object in a + /// // different namespace + /// } + /// @endcode + AmbiguousTagHiding }; typedef llvm::SmallVector DeclsTy; @@ -1132,7 +1154,7 @@ public: } bool isAmbiguous() const { - return getKind() >= FirstAmbiguous; + return getKind() == Ambiguous; } LookupKind getKind() const { @@ -1140,6 +1162,11 @@ public: return Kind; } + AmbiguityKind getAmbiguityKind() const { + assert(isAmbiguous()); + return Ambiguity; + } + iterator begin() const { return Decls.begin(); } iterator end() const { return Decls.end(); } @@ -1158,6 +1185,25 @@ public: Kind = Found; } + /// \brief Add all the declarations from another set of lookup + /// results. + void addAllDecls(const LookupResult &Other) { + Decls.append(Other.begin(), Other.end()); + Kind = Found; + } + + /// \brief Hides a set of declarations. + template void hideDecls(const NamedDeclSet &Set) { + unsigned I = 0, N = Decls.size(); + while (I < N) { + if (Set.count(Decls[I])) + Decls[I] = Decls[--N]; + else + I++; + } + Decls.set_size(N); + } + /// \brief Resolves the kind of the lookup, possibly hiding decls. /// /// This should be called in any environment where lookup might @@ -1181,6 +1227,11 @@ public: return *Decls.begin(); } + /// \brief Asks if the result is a single tag decl. + bool isSingleTagDecl() const { + return getKind() == Found && isa(getFoundDecl()); + } + /// \brief Make these results show that the name was found in /// base classes of different types. /// @@ -1193,6 +1244,13 @@ public: /// The given paths object is copied and invalidated. void setAmbiguousBaseSubobjects(CXXBasePaths &P); + /// \brief Make these results show that the name was found in + /// different contexts and a tag decl was hidden by an ordinary + /// decl in a different context. + void setAmbiguousQualifiedTagHiding() { + setAmbiguous(AmbiguousTagHiding); + } + /// \brief Clears out any current state. void clear() { Kind = NotFound; @@ -1204,6 +1262,11 @@ public: void print(llvm::raw_ostream &); private: + void setAmbiguous(AmbiguityKind AK) { + Kind = Ambiguous; + Ambiguity = AK; + } + void addDeclsFromBasePaths(const CXXBasePaths &P); // Sanity checks. @@ -1211,14 +1274,17 @@ public: assert(Kind != NotFound || Decls.size() == 0); assert(Kind != Found || Decls.size() == 1); assert(Kind == NotFound || Kind == Found || - Kind == AmbiguousBaseSubobjects || Decls.size() > 1); - assert((Paths != NULL) == (Kind == AmbiguousBaseSubobjectTypes || - Kind == AmbiguousBaseSubobjects)); + (Kind == Ambiguous && Ambiguity == AmbiguousBaseSubobjects) + || Decls.size() > 1); + assert((Paths != NULL) == (Kind == Ambiguous && + (Ambiguity == AmbiguousBaseSubobjectTypes || + Ambiguity == AmbiguousBaseSubobjects))); } static void deletePaths(CXXBasePaths *); LookupKind Kind; + AmbiguityKind Ambiguity; // ill-defined unless ambiguous DeclsTy Decls; CXXBasePaths *Paths; }; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 798238728184..f12f2b998598 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -94,9 +94,15 @@ Sema::TypeTy *Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc, case LookupResult::FoundOverloaded: return 0; - case LookupResult::AmbiguousBaseSubobjectTypes: - case LookupResult::AmbiguousBaseSubobjects: - case LookupResult::AmbiguousReference: { + case LookupResult::Ambiguous: { + // Recover from type-hiding ambiguities by hiding the type. We'll + // do the lookup again when looking for an object, and we can + // diagnose the error then. If we don't do this, then the error + // about hiding the type will be immediately followed by an error + // that only makes sense if the identifier was treated like a type. + if (Result.getAmbiguityKind() == LookupResult::AmbiguousTagHiding) + return 0; + // Look to see if we have a type anywhere in the list of results. for (LookupResult::iterator Res = Result.begin(), ResEnd = Result.end(); Res != ResEnd; ++Res) { @@ -4129,6 +4135,8 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, bool isStdBadAlloc = false; bool Invalid = false; + bool RedeclarationOnly = (TUK != TUK_Reference); + if (Name && SS.isNotEmpty()) { // We have a nested-name tag ('struct foo::bar'). @@ -4155,11 +4163,18 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SearchDC = DC; // Look-up name inside 'foo::'. LookupResult R; - LookupQualifiedName(R, DC, Name, LookupTagName, true); - PrevDecl = dyn_cast_or_null(R.getAsSingleDecl(Context)); + LookupQualifiedName(R, DC, Name, LookupTagName, RedeclarationOnly); + + if (R.isAmbiguous()) { + DiagnoseAmbiguousLookup(R, Name, NameLoc, SS.getRange()); + return DeclPtrTy(); + } + + if (R.getKind() == LookupResult::Found) + PrevDecl = dyn_cast(R.getFoundDecl()); // A tag 'foo::bar' must already exist. - if (PrevDecl == 0) { + if (!PrevDecl) { Diag(NameLoc, diag::err_not_tag_in_scope) << Name << SS.getRange(); Name = 0; Invalid = true; @@ -4172,8 +4187,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // shouldn't be. Doing so can result in ambiguities that we // shouldn't be diagnosing. LookupResult R; - LookupName(R, S, Name, LookupTagName, - /*RedeclarationOnly=*/(TUK != TUK_Reference)); + LookupName(R, S, Name, LookupTagName, RedeclarationOnly); if (R.isAmbiguous()) { DiagnoseAmbiguousLookup(R, Name, NameLoc); // FIXME: This is not best way to recover from case like: diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index aad12ccaa839..dd877c16fba7 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -25,6 +25,7 @@ #include "clang/Basic/LangOptions.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/ErrorHandling.h" #include #include #include @@ -162,6 +163,9 @@ void Sema::LookupResult::resolveKind() { // Fast case: no possible ambiguity. if (N <= 1) return; + // Don't do any extra resolution if we've already resolved as ambiguous. + if (Kind == Ambiguous) return; + llvm::SmallPtrSet Unique; bool Ambiguous = false; @@ -218,11 +222,11 @@ void Sema::LookupResult::resolveKind() { Ambiguous = true; if (Ambiguous) - Kind = AmbiguousReference; + setAmbiguous(LookupResult::AmbiguousReference); else if (N > 1) - Kind = FoundOverloaded; + Kind = LookupResult::FoundOverloaded; else - Kind = Found; + Kind = LookupResult::Found; } /// @brief Converts the result of name lookup into a single (possible @@ -278,7 +282,7 @@ void Sema::LookupResult::setAmbiguousBaseSubobjects(CXXBasePaths &P) { Paths->swap(P); addDeclsFromBasePaths(*Paths); resolveKind(); - Kind = AmbiguousBaseSubobjects; + setAmbiguous(AmbiguousBaseSubobjects); } void Sema::LookupResult::setAmbiguousBaseSubobjectTypes(CXXBasePaths &P) { @@ -286,7 +290,7 @@ void Sema::LookupResult::setAmbiguousBaseSubobjectTypes(CXXBasePaths &P) { Paths->swap(P); addDeclsFromBasePaths(*Paths); resolveKind(); - Kind = AmbiguousBaseSubobjectTypes; + setAmbiguous(AmbiguousBaseSubobjectTypes); } void Sema::LookupResult::print(llvm::raw_ostream &Out) { @@ -644,6 +648,120 @@ bool Sema::LookupName(LookupResult &R, Scope *S, DeclarationName Name, return false; } +/// @brief Perform qualified name lookup in the namespaces nominated by +/// using directives by the given context. +/// +/// C++98 [namespace.qual]p2: +/// Given X::m (where X is a user-declared namespace), or given ::m +/// (where X is the global namespace), let S be the set of all +/// declarations of m in X and in the transitive closure of all +/// namespaces nominated by using-directives in X and its used +/// namespaces, except that using-directives are ignored in any +/// namespace, including X, directly containing one or more +/// declarations of m. No namespace is searched more than once in +/// the lookup of a name. If S is the empty set, the program is +/// ill-formed. Otherwise, if S has exactly one member, or if the +/// context of the reference is a using-declaration +/// (namespace.udecl), S is the required set of declarations of +/// m. Otherwise if the use of m is not one that allows a unique +/// declaration to be chosen from S, the program is ill-formed. +/// C++98 [namespace.qual]p5: +/// During the lookup of a qualified namespace member name, if the +/// lookup finds more than one declaration of the member, and if one +/// declaration introduces a class name or enumeration name and the +/// other declarations either introduce the same object, the same +/// enumerator or a set of functions, the non-type name hides the +/// class or enumeration name if and only if the declarations are +/// from the same namespace; otherwise (the declarations are from +/// different namespaces), the program is ill-formed. +static bool LookupQualifiedNameInUsingDirectives(Sema::LookupResult &R, + DeclContext *StartDC, + DeclarationName Name, + Sema::LookupNameKind NameKind, + unsigned IDNS) { + assert(StartDC->isFileContext() && "start context is not a file context"); + + DeclContext::udir_iterator I = StartDC->using_directives_begin(); + DeclContext::udir_iterator E = StartDC->using_directives_end(); + + if (I == E) return false; + + // We have at least added all these contexts to the queue. + llvm::DenseSet Visited; + Visited.insert(StartDC); + + // We have not yet looked into these namespaces, much less added + // their "using-children" to the queue. + llvm::SmallVector Queue; + + // We have already looked into the initial namespace; seed the queue + // with its using-children. + for (; I != E; ++I) { + NamespaceDecl *ND = (*I)->getNominatedNamespace(); + if (Visited.insert(ND).second) + Queue.push_back(ND); + } + + // The easiest way to implement the restriction in [namespace.qual]p5 + // is to check whether any of the individual results found a tag + // and, if so, to declare an ambiguity if the final result is not + // a tag. + bool FoundTag = false; + bool FoundNonTag = false; + + Sema::LookupResult LocalR; + + bool Found = false; + while (!Queue.empty()) { + NamespaceDecl *ND = Queue.back(); + Queue.pop_back(); + + // We go through some convolutions here to avoid copying results + // between LookupResults. + bool UseLocal = !R.empty(); + Sema::LookupResult &DirectR = UseLocal ? LocalR : R; + bool FoundDirect = LookupDirect(DirectR, ND, Name, NameKind, IDNS); + + if (FoundDirect) { + // First do any local hiding. + DirectR.resolveKind(); + + // If the local result is a tag, remember that. + if (DirectR.isSingleTagDecl()) + FoundTag = true; + else + FoundNonTag = true; + + // Append the local results to the total results if necessary. + if (UseLocal) { + R.addAllDecls(LocalR); + LocalR.clear(); + } + } + + // If we find names in this namespace, ignore its using directives. + if (FoundDirect) { + Found = true; + continue; + } + + for (llvm::tie(I,E) = ND->getUsingDirectives(); I != E; ++I) { + NamespaceDecl *Nom = (*I)->getNominatedNamespace(); + if (Visited.insert(Nom).second) + Queue.push_back(Nom); + } + } + + if (Found) { + if (FoundTag && FoundNonTag) + R.setAmbiguousQualifiedTagHiding(); + else + R.resolveKind(); + } + + return Found; +} + /// @brief Perform qualified name lookup into a given context. /// /// Qualified name lookup (C++ [basic.lookup.qual]) is used to find @@ -704,9 +822,26 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, return true; } + // Don't descend into implied contexts for redeclarations. + // C++98 [namespace.qual]p6: + // In a declaration for a namespace member in which the + // declarator-id is a qualified-id, given that the qualified-id + // for the namespace member has the form + // nested-name-specifier unqualified-id + // the unqualified-id shall name a member of the namespace + // designated by the nested-name-specifier. + // See also [class.mfct]p5 and [class.static.data]p2. + if (RedeclarationOnly) + return false; + + // If this is a namespace, look it up in + if (LookupCtx->isFileContext()) + return LookupQualifiedNameInUsingDirectives(R, LookupCtx, Name, NameKind, + IDNS); + // If this isn't a C++ class, we aren't allowed to look into base // classes, we're done. - if (RedeclarationOnly || !isa(LookupCtx)) + if (!isa(LookupCtx)) return false; // Perform lookup into our base classes. @@ -895,31 +1030,32 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result, DeclarationName Name, SourceRange LookupRange) { assert(Result.isAmbiguous() && "Lookup result must be ambiguous"); - if (CXXBasePaths *Paths = Result.getBasePaths()) { - if (Result.getKind() == LookupResult::AmbiguousBaseSubobjects) { - QualType SubobjectType = Paths->front().back().Base->getType(); - Diag(NameLoc, diag::err_ambiguous_member_multiple_subobjects) - << Name << SubobjectType << getAmbiguousPathsDisplayString(*Paths) - << LookupRange; - - DeclContext::lookup_iterator Found = Paths->front().Decls.first; - while (isa(*Found) && - cast(*Found)->isStatic()) - ++Found; - - Diag((*Found)->getLocation(), diag::note_ambiguous_member_found); - - return true; - } - - assert(Result.getKind() == LookupResult::AmbiguousBaseSubobjectTypes && - "Unhandled form of name lookup ambiguity"); + switch (Result.getAmbiguityKind()) { + case LookupResult::AmbiguousBaseSubobjects: { + CXXBasePaths *Paths = Result.getBasePaths(); + QualType SubobjectType = Paths->front().back().Base->getType(); + Diag(NameLoc, diag::err_ambiguous_member_multiple_subobjects) + << Name << SubobjectType << getAmbiguousPathsDisplayString(*Paths) + << LookupRange; + + DeclContext::lookup_iterator Found = Paths->front().Decls.first; + while (isa(*Found) && + cast(*Found)->isStatic()) + ++Found; + + Diag((*Found)->getLocation(), diag::note_ambiguous_member_found); + + return true; + } + case LookupResult::AmbiguousBaseSubobjectTypes: { Diag(NameLoc, diag::err_ambiguous_member_multiple_subobject_types) << Name << LookupRange; - + + CXXBasePaths *Paths = Result.getBasePaths(); std::set DeclsPrinted; - for (CXXBasePaths::paths_iterator Path = Paths->begin(), PathEnd = Paths->end(); + for (CXXBasePaths::paths_iterator Path = Paths->begin(), + PathEnd = Paths->end(); Path != PathEnd; ++Path) { Decl *D = *Path->Decls.first; if (DeclsPrinted.insert(D).second) @@ -929,15 +1065,40 @@ bool Sema::DiagnoseAmbiguousLookup(LookupResult &Result, DeclarationName Name, return true; } - assert(Result.getKind() == LookupResult::AmbiguousReference && - "unhandled form of name lookup ambiguity"); - Diag(NameLoc, diag::err_ambiguous_reference) << Name << LookupRange; + case LookupResult::AmbiguousTagHiding: { + Diag(NameLoc, diag::err_ambiguous_tag_hiding) << Name << LookupRange; + llvm::SmallPtrSet TagDecls; + + LookupResult::iterator DI, DE = Result.end(); + for (DI = Result.begin(); DI != DE; ++DI) + if (TagDecl *TD = dyn_cast(*DI)) { + TagDecls.insert(TD); + Diag(TD->getLocation(), diag::note_hidden_tag); + } + + for (DI = Result.begin(); DI != DE; ++DI) + if (!isa(*DI)) + Diag((*DI)->getLocation(), diag::note_hiding_object); + + // For recovery purposes, go ahead and implement the hiding. + Result.hideDecls(TagDecls); + + return true; + } + + case LookupResult::AmbiguousReference: { + Diag(NameLoc, diag::err_ambiguous_reference) << Name << LookupRange; - LookupResult::iterator DI = Result.begin(), DE = Result.end(); - for (; DI != DE; ++DI) - Diag((*DI)->getLocation(), diag::note_ambiguous_candidate) << *DI; + LookupResult::iterator DI = Result.begin(), DE = Result.end(); + for (; DI != DE; ++DI) + Diag((*DI)->getLocation(), diag::note_ambiguous_candidate) << *DI; + return true; + } + } + + llvm::llvm_unreachable("unknown ambiguity kind"); return true; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 8f515051bc37..8a12ac11684d 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3858,9 +3858,7 @@ Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II, Referenced = *Result.begin(); break; - case LookupResult::AmbiguousBaseSubobjectTypes: - case LookupResult::AmbiguousBaseSubobjects: - case LookupResult::AmbiguousReference: + case LookupResult::Ambiguous: DiagnoseAmbiguousLookup(Result, Name, Range.getEnd(), Range); return QualType(); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 676771208587..6a5db5f2ad38 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -194,7 +194,13 @@ QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS, case DeclSpec::TST_union: case DeclSpec::TST_struct: { Decl *D = static_cast(DS.getTypeRep()); - assert(D && "Didn't get a decl for a class/enum/union/struct?"); + if (!D) { + // This can happen in C++ with ambiguous lookups. + Result = Context.IntTy; + isInvalid = true; + break; + } + assert(DS.getTypeSpecWidth() == 0 && DS.getTypeSpecComplex() == 0 && DS.getTypeSpecSign() == 0 && "Can't handle qualifiers on typedef names yet!"); diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp new file mode 100644 index 000000000000..b32948b4a608 --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p2.cpp @@ -0,0 +1,65 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +namespace Ints { + int zero = 0; // expected-note {{candidate found by name lookup is 'Ints::zero'}} + void f(int); // expected-note 3 {{candidate function}} + void g(int); +} + +namespace Floats { + float zero = 0.0f; // expected-note {{candidate found by name lookup is 'Floats::zero'}} + void f(float); // expected-note 3 {{candidate function}} + void g(float); +} + +namespace Numbers { + using namespace Ints; + using namespace Floats; +} + +void test() { + int i = Ints::zero; + Ints::f(i); + + float f = Floats::zero; + Floats::f(f); + + double n = Numbers::zero; // expected-error {{reference to 'zero' is ambiguous}} + Numbers::f(n); // expected-error{{call to 'f' is ambiguous}} + Numbers::f(i); + Numbers::f(f); +} + +namespace Numbers { + struct Number { + explicit Number(double d) : d(d) {} + double d; + }; + Number zero(0.0f); + void g(Number); +} + +void test2() { + Numbers::Number n = Numbers::zero; + Numbers::f(n); // expected-error {{no matching function for call to 'f'}} + Numbers::g(n); +} + +namespace Numbers2 { + using Numbers::f; + using Numbers::g; +} + +void test3() { + Numbers::Number n = Numbers::zero; + Numbers2::f(n); // expected-error {{no matching function for call to 'f'}} + Numbers2::g(n); + + int i = Ints::zero; + Numbers2::f(i); + Numbers2::g(i); // expected-error {{incompatible type passing 'int'}} + + float f = Floats::zero; + Numbers2::f(f); + Numbers2::g(f); // expected-error {{incompatible type passing 'float'}} +} diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p3.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p3.cpp new file mode 100644 index 000000000000..7a51a7bb1df0 --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p3.cpp @@ -0,0 +1,41 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +// This is basically paraphrased from the standard. + +namespace Root { + int i = 0; + void f(); +} + +namespace A { + using namespace Root; +} + +namespace B { + using namespace Root; +} + +namespace AB { + using namespace A; + using namespace B; +} + +void test() { + if (AB::i) + AB::f(); +} + +namespace C { + using Root::i; + using Root::f; +} + +namespace AC { + using namespace A; + using namespace C; +} + +void test2() { + if (AC::i) + AC::f(); +} diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p4.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p4.cpp new file mode 100644 index 000000000000..2c0ce80d8ce6 --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p4.cpp @@ -0,0 +1,25 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +namespace A { + int a; +} + +namespace C { + int c; +} + +namespace B { + using namespace C; + int b; +} + +namespace C { + using namespace B; + using namespace A; +} + +void test() { + C::a++; + C::b++; + C::c++; +} diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p5.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p5.cpp new file mode 100644 index 000000000000..78af521c9103 --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/namespace.qual/p5.cpp @@ -0,0 +1,35 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +namespace A { + struct x {}; // expected-note {{candidate found by name lookup is 'A::x'}} + int x; // expected-note {{candidate found by name lookup is 'A::x'}} + + struct y {}; // expected-note {{type declaration hidden}} + + struct z; + void z(float); +} + +namespace B { + struct x {}; // expected-note {{candidate found by name lookup is 'B::x'}} + float x; // expected-note {{candidate found by name lookup is 'B::x'}} + + float y; // expected-note {{declaration hides type}} + + void z(int); +} + +namespace AB { + using namespace A; + using namespace B; +} + +void test() { + struct AB::x foo; // expected-error {{reference to 'x' is ambiguous}} + int i = AB::x; // expected-error {{reference to 'x' is ambiguous}} + + struct AB::y bar; + float f = AB::y; // expected-error {{a type named 'y' is hidden by a declaration in a different namespace}} + AB::z(i); + AB::z(f); +}