From: Douglas Gregor Date: Tue, 23 Feb 2010 00:15:22 +0000 (+0000) Subject: Implement crazy destructor name lookup semantics differently in X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=93649fdc5e0c46e26bcba06ad39aa80196d3df27;p=clang Implement crazy destructor name lookup semantics differently in C++98/03 and C++0x, since the '0x semantics break valid C++98/03 code. This new mess is tracked by core issue 399, which is still unresolved. Fixes PR6358 and PR6359. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96836 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 08a1b2a8e9..9d41f9d0db 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -49,7 +49,7 @@ Action::TypeTy *Sema::getDestructorName(SourceLocation TildeLoc, // s->N::S::~S(); // } // - // + // See also PR6358 and PR6359. QualType SearchType; DeclContext *LookupCtx = 0; bool isDependent = false; @@ -62,7 +62,39 @@ Action::TypeTy *Sema::getDestructorName(SourceLocation TildeLoc, SearchType = GetTypeFromParser(ObjectTypePtr); if (SS.isSet()) { - // C++ [basic.lookup.qual]p6: + NestedNameSpecifier *NNS = (NestedNameSpecifier *)SS.getScopeRep(); + + bool AlreadySearched = false; + bool LookAtPrefix = true; + if (!getLangOptions().CPlusPlus0x) { + // C++ [basic.lookup.qual]p6: + // If a pseudo-destructor-name (5.2.4) contains a nested-name-specifier, + // the type-names are looked up as types in the scope designated by the + // nested-name-specifier. In a qualified-id of the form: + // + // ::[opt] nested-name-specifier ̃ class-name + // + // where the nested-name-specifier designates a namespace scope, and in + // a qualified-id of the form: + // + // ::opt nested-name-specifier class-name :: ̃ class-name + // + // the class-names are looked up as types in the scope designated by + // the nested-name-specifier. + // + // Here, we check the first case (completely) and determine whether the + // code below is permitted to look at the prefix of the + // nested-name-specifier (as we do in C++0x). + DeclContext *DC = computeDeclContext(SS, EnteringContext); + if (DC && DC->isFileContext()) { + AlreadySearched = true; + LookupCtx = DC; + isDependent = false; + } else if (DC && isa(DC)) + LookAtPrefix = false; + } + + // C++0x [basic.lookup.qual]p6: // If a pseudo-destructor-name (5.2.4) contains a // nested-name-specifier, the type-names are looked up as types // in the scope designated by the nested-name-specifier. Similarly, in @@ -72,23 +104,33 @@ Action::TypeTy *Sema::getDestructorName(SourceLocation TildeLoc, // // the second class-name is looked up in the same scope as the first. // - // FIXME: We don't implement this, because it breaks lots of - // perfectly reasonable code that no other compilers diagnose. The - // issue is that the first class-name is looked up as a - // nested-name-specifier, so we ignore value declarations, but the - // second lookup is presumably an ordinary name lookup. Hence, we - // end up finding values (say, a function) and complain. See PRs - // 6358 and 6359 for examples of such code. DPG to investigate - // further. - if (ObjectTypePtr) { + // To implement this, we look at the prefix of the + // nested-name-specifier we were given, and determine the lookup + // context from that. + // + // We also fold in the second case from the C++03 rules quoted further + // above. + NestedNameSpecifier *Prefix = 0; + if (AlreadySearched) { + // Nothing left to do. + } else if (LookAtPrefix && (Prefix = NNS->getPrefix())) { + CXXScopeSpec PrefixSS; + PrefixSS.setScopeRep(Prefix); + LookupCtx = computeDeclContext(PrefixSS, EnteringContext); + isDependent = isDependentScopeSpecifier(PrefixSS); + } else if (getLangOptions().CPlusPlus0x && + (LookupCtx = computeDeclContext(SS, EnteringContext))) { + if (!LookupCtx->isTranslationUnit()) + LookupCtx = LookupCtx->getParent(); + isDependent = LookupCtx && LookupCtx->isDependentContext(); + } else if (ObjectTypePtr) { LookupCtx = computeDeclContext(SearchType); isDependent = SearchType->isDependentType(); } else { LookupCtx = computeDeclContext(SS, EnteringContext); - if (LookupCtx) - isDependent = LookupCtx->isDependentContext(); + isDependent = LookupCtx && LookupCtx->isDependentContext(); } - + LookInScope = (LookupCtx == 0) && !isDependent; } else if (ObjectTypePtr) { // C++ [basic.lookup.classref]p3: diff --git a/test/CXX/basic/basic.lookup/basic.lookup.classref/p3.cpp b/test/CXX/basic/basic.lookup/basic.lookup.classref/p3.cpp new file mode 100644 index 0000000000..4a0b387379 --- /dev/null +++ b/test/CXX/basic/basic.lookup/basic.lookup.classref/p3.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// C++0x [basic.lookup.classref]p3: +// If the unqualified-id is ∼type-name, the type-name is looked up in the +// context of the entire postfix-expression. If the type T of the object +// expression is of a class type C, the type-name is also looked up in the +// scope of class C. At least one of the lookups shall find a name that +// refers to (possibly cv-qualified) T. + +// From core issue 305 +struct A { +}; + +struct C { + struct A {}; + void f (); +}; + +void C::f () { + ::A *a; + a->~A (); +} + +// From core issue 414 +struct X {}; +void f() { + X x; + struct X {}; + x.~X(); +} diff --git a/test/CXX/basic/basic.lookup/basic.lookup.qual/p6-0x.cpp b/test/CXX/basic/basic.lookup/basic.lookup.qual/p6-0x.cpp new file mode 100644 index 0000000000..35efba571d --- /dev/null +++ b/test/CXX/basic/basic.lookup/basic.lookup.qual/p6-0x.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s +struct C { + typedef int I; +}; + +typedef int I1, I2; +extern int* p; +extern int* q; + +void f() { + p->C::I::~I(); + q->I1::~I2(); +} + +struct A { + ~A(); +}; + +typedef A AB; +int main() { + AB *p; + p->AB::~AB(); +} diff --git a/test/CXX/basic/basic.lookup/basic.lookup.qual/p6.cpp b/test/CXX/basic/basic.lookup/basic.lookup.qual/p6.cpp new file mode 100644 index 0000000000..633d5cda99 --- /dev/null +++ b/test/CXX/basic/basic.lookup/basic.lookup.qual/p6.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct C { + typedef int I; +}; + +typedef int I1, I2; +extern int* p; +extern int* q; + +void f() { + p->C::I::~I(); + q->I1::~I2(); +} + +struct A { + ~A(); +}; + +typedef A AB; +int main() { + AB *p; + p->AB::~AB(); // expected-error{{identifier 'AB' in pseudo-destructor expression does not name a type}} +} diff --git a/test/SemaCXX/pseudo-destructors.cpp b/test/SemaCXX/pseudo-destructors.cpp index 2cc233318e..de9ca2d300 100644 --- a/test/SemaCXX/pseudo-destructors.cpp +++ b/test/SemaCXX/pseudo-destructors.cpp @@ -29,7 +29,7 @@ void f(A* a, Foo *f, int *i, double *d) { g().~Bar(); // expected-error{{non-scalar}} f->::~Bar(); - f->N::~Wibble(); // FIXME: Cannot use typedef name in destructor id. + f->N::~Wibble(); // FIXME: technically, Wibble isn't a class-name f->::~Bar(17, 42); // expected-error{{cannot have any arguments}}