From 7edb5fdf9703e1abd780417db691b77d5fcbc610 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 26 Jan 2010 07:16:45 +0000 Subject: [PATCH] Handle redeclarations found by ADL deterministically and reasonably. This solution relies on an O(n) scan of redeclarations, which means it might scale poorly in crazy cases with tons of redeclarations brought in by a ton of distinct associated namespaces. I believe that avoiding this is not worth the common-case cost. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94530 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/Lookup.h | 38 +++++++++++++++++++++++++++ lib/Sema/Sema.h | 5 ++-- lib/Sema/SemaLookup.cpp | 49 ++++++++++++++++++++++++++++++----- lib/Sema/SemaOverload.cpp | 11 ++++---- test/SemaCXX/using-decl-1.cpp | 18 +++++++++++++ 5 files changed, 105 insertions(+), 16 deletions(-) diff --git a/lib/Sema/Lookup.h b/lib/Sema/Lookup.h index 0c0f363cbd..fa6f037026 100644 --- a/lib/Sema/Lookup.h +++ b/lib/Sema/Lookup.h @@ -587,6 +587,44 @@ private: virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass) = 0; }; + +/// \brief A class for storing results from argument-dependent lookup. +class ADLResult { +private: + /// A map from canonical decls to the 'most recent' decl. + llvm::DenseMap Decls; + +public: + /// Adds a new ADL candidate to this map. + void insert(NamedDecl *D); + + /// Removes any data associated with a given decl. + void erase(NamedDecl *D) { + Decls.erase(cast(D->getCanonicalDecl())); + } + + class iterator { + typedef llvm::DenseMap::iterator inner_iterator; + inner_iterator iter; + + friend class ADLResult; + iterator(const inner_iterator &iter) : iter(iter) {} + public: + iterator() {} + + iterator &operator++() { ++iter; return *this; } + iterator operator++(int) { return iterator(iter++); } + + NamedDecl *operator*() const { return iter->second; } + + bool operator==(const iterator &other) const { return iter == other.iter; } + bool operator!=(const iterator &other) const { return iter != other.iter; } + }; + + iterator begin() { return iterator(Decls.begin()); } + iterator end() { return iterator(Decls.end()); } +}; + } #endif diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 42dac8fafc..d041f54a02 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -103,6 +103,7 @@ namespace clang { class InitializationSequence; class VisibleDeclConsumer; class TargetAttributesSema; + class ADLResult; /// BlockSemaInfo - When a block is being parsed, this contains information /// about the block. It is pointed to from Sema::CurBlock. @@ -950,8 +951,6 @@ public: // Members have to be NamespaceDecl* or TranslationUnitDecl*. // TODO: make this is a typesafe union. typedef llvm::SmallPtrSet AssociatedNamespaceSet; - // Members have to be a function or function template. - typedef llvm::SmallPtrSet ADLFunctionSet; typedef llvm::SmallPtrSet AssociatedClassSet; void AddOverloadCandidate(NamedDecl *Function, @@ -1240,7 +1239,7 @@ public: void ArgumentDependentLookup(DeclarationName Name, bool Operator, Expr **Args, unsigned NumArgs, - ADLFunctionSet &Functions); + ADLResult &Functions); void LookupVisibleDecls(Scope *S, LookupNameKind Kind, VisibleDeclConsumer &Consumer); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index c61d666c4b..9b91e22739 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1731,9 +1731,46 @@ void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S, } } +void ADLResult::insert(NamedDecl *New) { + NamedDecl *&Old = Decls[cast(New->getCanonicalDecl())]; + + // If we haven't yet seen a decl for this key, or the last decl + // was exactly this one, we're done. + if (Old == 0 || Old == New) { + Old = New; + return; + } + + // Otherwise, decide which is a more recent redeclaration. + FunctionDecl *OldFD, *NewFD; + if (isa(New)) { + OldFD = cast(Old)->getTemplatedDecl(); + NewFD = cast(New)->getTemplatedDecl(); + } else { + OldFD = cast(Old); + NewFD = cast(New); + } + + FunctionDecl *Cursor = NewFD; + while (true) { + Cursor = Cursor->getPreviousDeclaration(); + + // If we got to the end without finding OldFD, OldFD is the newer + // declaration; leave things as they are. + if (!Cursor) return; + + // If we do find OldFD, then NewFD is newer. + if (Cursor == OldFD) break; + + // Otherwise, keep looking. + } + + Old = New; +} + void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator, Expr **Args, unsigned NumArgs, - ADLFunctionSet &Functions) { + ADLResult &Result) { // Find all of the associated namespaces and classes based on the // arguments we have. AssociatedNamespaceSet AssociatedNamespaces; @@ -1788,17 +1825,15 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, bool Operator, if (isa(D)) D = cast(D)->getTargetDecl(); - // FIXME: canonical decls. - // See comment in AddArgumentDependentLookupCandidates(). - if (isa(D)) { if (Operator && !IsAcceptableNonMemberOperatorCandidate(cast(D), T1, T2, Context)) continue; - Functions.insert(D); - } else if (isa(D)) - Functions.insert(D); + } else if (!isa(D)) + continue; + + Result.insert(D); } } } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index a4c11b077a..0fba0c6228 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -4131,7 +4131,7 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name, const TemplateArgumentListInfo *ExplicitTemplateArgs, OverloadCandidateSet& CandidateSet, bool PartialOverloading) { - ADLFunctionSet Functions; + ADLResult Fns; // FIXME: This approach for uniquing ADL results (and removing // redundant candidates from the set) relies on pointer-equality, @@ -4141,22 +4141,21 @@ Sema::AddArgumentDependentLookupCandidates(DeclarationName Name, // we supposed to consider on ADL candidates, anyway? // FIXME: Pass in the explicit template arguments? - ArgumentDependentLookup(Name, Operator, Args, NumArgs, Functions); + ArgumentDependentLookup(Name, Operator, Args, NumArgs, Fns); // Erase all of the candidates we already knew about. for (OverloadCandidateSet::iterator Cand = CandidateSet.begin(), CandEnd = CandidateSet.end(); Cand != CandEnd; ++Cand) if (Cand->Function) { - Functions.erase(Cand->Function); + Fns.erase(Cand->Function); if (FunctionTemplateDecl *FunTmpl = Cand->Function->getPrimaryTemplate()) - Functions.erase(FunTmpl); + Fns.erase(FunTmpl); } // For each of the ADL candidates we found, add it to the overload // set. - for (ADLFunctionSet::iterator I = Functions.begin(), - E = Functions.end(); I != E; ++I) { + for (ADLResult::iterator I = Fns.begin(), E = Fns.end(); I != E; ++I) { if (FunctionDecl *FD = dyn_cast(*I)) { if (ExplicitTemplateArgs) continue; diff --git a/test/SemaCXX/using-decl-1.cpp b/test/SemaCXX/using-decl-1.cpp index eb41e6630f..30c4cfd997 100644 --- a/test/SemaCXX/using-decl-1.cpp +++ b/test/SemaCXX/using-decl-1.cpp @@ -77,3 +77,21 @@ namespace test0 { foo(*p); // expected-error {{no matching function for call to 'foo'}} } } + +// Redeclarations! +namespace test1 { + namespace ns0 { struct Foo {}; } + namespace A { void foo(ns0::Foo *p, int y, int z); } + namespace ns2 { using A::foo; } + namespace ns1 { struct Bar : ns0::Foo {}; } + namespace A { void foo(ns0::Foo *p, int y, int z = 0); } // expected-note {{candidate}} + namespace ns1 { using A::foo; } + namespace ns2 { struct Baz : ns1::Bar {}; } + namespace A { void foo(ns0::Foo *p, int y = 0, int z); } + + void test(ns2::Baz *p) { + foo(p, 0, 0); // okay! + foo(p, 0); // should be fine! + foo(p); // expected-error {{no matching function}} + } +} -- 2.50.1