forked from OSchip/llvm-project
Qualified lookup through using declarations. Diagnose a new type of ambiguity.
Split the various ambiguous result enumerators into their own enum. Tests for most of C++ [namespace.qual]. llvm-svn: 83700
This commit is contained in:
parent
b8120770b4
commit
6538c93050
|
@ -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<
|
||||
|
|
|
@ -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<NamedDecl*, 4> 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 <class NamedDeclSet> 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<TagDecl>(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;
|
||||
};
|
||||
|
|
|
@ -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<TagDecl>(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<TagDecl>(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:
|
||||
|
|
|
@ -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 <set>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
@ -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<NamedDecl*, 16> 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<DeclContext*> Visited;
|
||||
Visited.insert(StartDC);
|
||||
|
||||
// We have not yet looked into these namespaces, much less added
|
||||
// their "using-children" to the queue.
|
||||
llvm::SmallVector<NamespaceDecl*, 8> 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<CXXRecordDecl>(LookupCtx))
|
||||
if (!isa<CXXRecordDecl>(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<CXXMethodDecl>(*Found) &&
|
||||
cast<CXXMethodDecl>(*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<CXXMethodDecl>(*Found) &&
|
||||
cast<CXXMethodDecl>(*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<Decl *> 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<NamedDecl*,8> TagDecls;
|
||||
|
||||
LookupResult::iterator DI, DE = Result.end();
|
||||
for (DI = Result.begin(); DI != DE; ++DI)
|
||||
if (TagDecl *TD = dyn_cast<TagDecl>(*DI)) {
|
||||
TagDecls.insert(TD);
|
||||
Diag(TD->getLocation(), diag::note_hidden_tag);
|
||||
}
|
||||
|
||||
for (DI = Result.begin(); DI != DE; ++DI)
|
||||
if (!isa<TagDecl>(*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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -194,7 +194,13 @@ QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS,
|
|||
case DeclSpec::TST_union:
|
||||
case DeclSpec::TST_struct: {
|
||||
Decl *D = static_cast<Decl *>(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!");
|
||||
|
|
|
@ -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'}}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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++;
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue