[cfe-commits] r90843 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/Sema.h lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaTemplateInstantiateDecl.cpp test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3-cxx0x.cpp test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp test/SemaCXX/using-decl-templates.cpp
John McCall
rjmccall at apple.com
Mon Dec 7 23:46:18 PST 2009
Author: rjmccall
Date: Tue Dec 8 01:46:18 2009
New Revision: 90843
URL: http://llvm.org/viewvc/llvm-project?rev=90843&view=rev
Log:
Correctly implement the C++03 and 0x restrictions on class-member using
declarations.
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3-cxx0x.cpp
cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp
cfe/trunk/test/SemaCXX/using-decl-templates.cpp
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=90843&r1=90842&r2=90843&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Dec 8 01:46:18 2009
@@ -105,7 +105,11 @@
"'typename' keyword used on a non-type">;
def err_using_dependent_value_is_type : Error<
"dependent using declaration resolved to type without 'typename'">;
-def err_using_decl_nested_name_specifier_is_not_a_base_class : Error<
+def err_using_decl_nested_name_specifier_is_not_class : Error<
+ "using declaration in class refers into '%0', which is not a class">;
+def err_using_decl_nested_name_specifier_is_current_class : Error<
+ "using declaration refers to its own class">;
+def err_using_decl_nested_name_specifier_is_not_base_class : Error<
"using declaration refers into '%0', which is not a base class of %1">;
def err_using_decl_can_not_refer_to_class_member : Error<
"using declaration can not refer to class member">;
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=90843&r1=90842&r2=90843&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Tue Dec 8 01:46:18 2009
@@ -1730,6 +1730,9 @@
SourceLocation IdentLoc,
IdentifierInfo *Ident);
+ UsingShadowDecl *BuildUsingShadowDecl(Scope *S, AccessSpecifier AS,
+ UsingDecl *UD, NamedDecl *Target);
+
bool CheckUsingDeclQualifier(SourceLocation UsingLoc,
const CXXScopeSpec &SS,
SourceLocation NameLoc);
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=90843&r1=90842&r2=90843&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Tue Dec 8 01:46:18 2009
@@ -2889,6 +2889,9 @@
break;
case UnqualifiedId::IK_ConstructorName:
+ // C++0x inherited constructors.
+ if (getLangOptions().CPlusPlus0x) break;
+
Diag(Name.getSourceRange().getBegin(), diag::err_using_decl_constructor)
<< SS.getRange();
return DeclPtrTy();
@@ -2905,6 +2908,9 @@
}
DeclarationName TargetName = GetNameFromUnqualifiedId(Name);
+ if (!TargetName)
+ return DeclPtrTy();
+
NamedDecl *UD = BuildUsingDeclaration(S, AS, UsingLoc, SS,
Name.getSourceRange().getBegin(),
TargetName, AttrList,
@@ -2917,28 +2923,66 @@
}
/// Builds a shadow declaration corresponding to a 'using' declaration.
-static UsingShadowDecl *BuildUsingShadowDecl(Sema &SemaRef, Scope *S,
- AccessSpecifier AS,
- UsingDecl *UD, NamedDecl *Orig) {
+UsingShadowDecl *Sema::BuildUsingShadowDecl(Scope *S,
+ AccessSpecifier AS,
+ UsingDecl *UD,
+ NamedDecl *Orig) {
// FIXME: diagnose hiding, collisions
// If we resolved to another shadow declaration, just coalesce them.
- if (isa<UsingShadowDecl>(Orig)) {
- Orig = cast<UsingShadowDecl>(Orig)->getTargetDecl();
- assert(!isa<UsingShadowDecl>(Orig) && "nested shadow declaration");
+ NamedDecl *Target = Orig;
+ if (isa<UsingShadowDecl>(Target)) {
+ Target = cast<UsingShadowDecl>(Target)->getTargetDecl();
+ assert(!isa<UsingShadowDecl>(Target) && "nested shadow declaration");
}
UsingShadowDecl *Shadow
- = UsingShadowDecl::Create(SemaRef.Context, SemaRef.CurContext,
- UD->getLocation(), UD, Orig);
+ = UsingShadowDecl::Create(Context, CurContext,
+ UD->getLocation(), UD, Target);
UD->addShadowDecl(Shadow);
if (S)
- SemaRef.PushOnScopeChains(Shadow, S);
+ PushOnScopeChains(Shadow, S);
else
- SemaRef.CurContext->addDecl(Shadow);
+ CurContext->addDecl(Shadow);
Shadow->setAccess(AS);
+ if (Orig->isInvalidDecl() || UD->isInvalidDecl())
+ Shadow->setInvalidDecl();
+
+ // If we haven't already declared the shadow decl invalid, check
+ // whether the decl comes from a base class of the current class.
+ // We don't have to do this in C++0x because we do the check once on
+ // the qualifier.
+ else if (!getLangOptions().CPlusPlus0x && CurContext->isRecord()) {
+ DeclContext *OrigDC = Orig->getDeclContext();
+
+ // Handle enums and anonymous structs.
+ if (isa<EnumDecl>(OrigDC)) OrigDC = OrigDC->getParent();
+ CXXRecordDecl *OrigRec = cast<CXXRecordDecl>(OrigDC);
+ while (OrigRec->isAnonymousStructOrUnion())
+ OrigRec = cast<CXXRecordDecl>(OrigRec->getDeclContext());
+
+ if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom(OrigRec)) {
+ if (OrigDC == CurContext) {
+ Diag(UD->getLocation(),
+ diag::err_using_decl_nested_name_specifier_is_current_class)
+ << UD->getNestedNameRange();
+ Diag(Orig->getLocation(), diag::note_using_decl_target);
+ Shadow->setInvalidDecl();
+ return Shadow;
+ }
+
+ Diag(UD->getNestedNameRange().getBegin(),
+ diag::err_using_decl_nested_name_specifier_is_not_base_class)
+ << UD->getTargetNestedNameDecl()
+ << cast<CXXRecordDecl>(CurContext)
+ << UD->getNestedNameRange();
+ Diag(Orig->getLocation(), diag::note_using_decl_target);
+ return Shadow;
+ }
+ }
+
return Shadow;
}
@@ -2998,41 +3042,19 @@
if (!LookupContext) return D;
UsingDecl *UD = cast<UsingDecl>(D);
- if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(CurContext)) {
- // C++0x N2914 [namespace.udecl]p3:
- // A using-declaration used as a member-declaration shall refer to a member
- // of a base class of the class being defined, shall refer to a member of an
- // anonymous union that is a member of a base class of the class being
- // defined, or shall refer to an enumerator for an enumeration type that is
- // a member of a base class of the class being defined.
-
- CXXRecordDecl *LookupRD = dyn_cast<CXXRecordDecl>(LookupContext);
- if (!LookupRD || !RD->isDerivedFrom(LookupRD)) {
- Diag(SS.getRange().getBegin(),
- diag::err_using_decl_nested_name_specifier_is_not_a_base_class)
- << NNS << RD->getDeclName();
- UD->setInvalidDecl();
- return UD;
- }
- } else {
- // C++0x N2914 [namespace.udecl]p8:
- // A using-declaration for a class member shall be a member-declaration.
- if (isa<CXXRecordDecl>(LookupContext)) {
- Diag(IdentLoc, diag::err_using_decl_can_not_refer_to_class_member)
- << SS.getRange();
- UD->setInvalidDecl();
- return UD;
- }
+ if (RequireCompleteDeclContext(SS)) {
+ UD->setInvalidDecl();
+ return UD;
}
- // Look up the target name. Unlike most lookups, we do not want to
- // hide tag declarations: tag names are visible through the using
- // declaration even if hidden by ordinary names.
+ // Look up the target name.
+
LookupResult R(*this, Name, IdentLoc, LookupOrdinaryName);
- // We don't hide tags behind ordinary decls if we're in a
- // non-dependent context, but in a dependent context, this is
- // important for the stability of two-phase lookup.
+ // Unlike most lookups, we don't always want to hide tag
+ // declarations: tag names are visible through the using declaration
+ // even if hidden by ordinary names, *except* in a dependent context
+ // where it's important for the sanity of two-phase lookup.
if (!IsInstantiation)
R.setHideTags(false);
@@ -3082,20 +3104,141 @@
}
for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
- BuildUsingShadowDecl(*this, S, AS, UD, *I);
+ BuildUsingShadowDecl(S, AS, UD, *I);
return UD;
}
+
/// Checks that the given nested-name qualifier used in a using decl
/// in the current context is appropriately related to the current
/// scope. If an error is found, diagnoses it and returns true.
bool Sema::CheckUsingDeclQualifier(SourceLocation UsingLoc,
const CXXScopeSpec &SS,
SourceLocation NameLoc) {
- // FIXME: implement
+ DeclContext *NamedContext = computeDeclContext(SS);
- return false;
+ if (!CurContext->isRecord()) {
+ // C++03 [namespace.udecl]p3:
+ // C++0x [namespace.udecl]p8:
+ // A using-declaration for a class member shall be a member-declaration.
+
+ // If we weren't able to compute a valid scope, it must be a
+ // dependent class scope.
+ if (!NamedContext || NamedContext->isRecord()) {
+ Diag(NameLoc, diag::err_using_decl_can_not_refer_to_class_member)
+ << SS.getRange();
+ return true;
+ }
+
+ // Otherwise, everything is known to be fine.
+ return false;
+ }
+
+ // The current scope is a record.
+
+ // If the named context is dependent, we can't decide much.
+ if (!NamedContext) {
+ // FIXME: in C++0x, we can diagnose if we can prove that the
+ // nested-name-specifier does not refer to a base class, which is
+ // still possible in some cases.
+
+ // Otherwise we have to conservatively report that things might be
+ // okay.
+ return false;
+ }
+
+ if (!NamedContext->isRecord()) {
+ // Ideally this would point at the last name in the specifier,
+ // but we don't have that level of source info.
+ Diag(SS.getRange().getBegin(),
+ diag::err_using_decl_nested_name_specifier_is_not_class)
+ << (NestedNameSpecifier*) SS.getScopeRep() << SS.getRange();
+ return true;
+ }
+
+ if (getLangOptions().CPlusPlus0x) {
+ // C++0x [namespace.udecl]p3:
+ // In a using-declaration used as a member-declaration, the
+ // nested-name-specifier shall name a base class of the class
+ // being defined.
+
+ if (cast<CXXRecordDecl>(CurContext)->isProvablyNotDerivedFrom(
+ cast<CXXRecordDecl>(NamedContext))) {
+ if (CurContext == NamedContext) {
+ Diag(NameLoc,
+ diag::err_using_decl_nested_name_specifier_is_current_class)
+ << SS.getRange();
+ return true;
+ }
+
+ Diag(SS.getRange().getBegin(),
+ diag::err_using_decl_nested_name_specifier_is_not_base_class)
+ << (NestedNameSpecifier*) SS.getScopeRep()
+ << cast<CXXRecordDecl>(CurContext)
+ << SS.getRange();
+ return true;
+ }
+
+ return false;
+ }
+
+ // C++03 [namespace.udecl]p4:
+ // A using-declaration used as a member-declaration shall refer
+ // to a member of a base class of the class being defined [etc.].
+
+ // Salient point: SS doesn't have to name a base class as long as
+ // lookup only finds members from base classes. Therefore we can
+ // diagnose here only if we can prove that that can't happen,
+ // i.e. if the class hierarchies provably don't intersect.
+
+ // TODO: it would be nice if "definitely valid" results were cached
+ // in the UsingDecl and UsingShadowDecl so that these checks didn't
+ // need to be repeated.
+
+ struct UserData {
+ llvm::DenseSet<const CXXRecordDecl*> Bases;
+
+ static bool collect(const CXXRecordDecl *Base, void *OpaqueData) {
+ UserData *Data = reinterpret_cast<UserData*>(OpaqueData);
+ Data->Bases.insert(Base);
+ return true;
+ }
+
+ bool hasDependentBases(const CXXRecordDecl *Class) {
+ return !Class->forallBases(collect, this);
+ }
+
+ /// Returns true if the base is dependent or is one of the
+ /// accumulated base classes.
+ static bool doesNotContain(const CXXRecordDecl *Base, void *OpaqueData) {
+ UserData *Data = reinterpret_cast<UserData*>(OpaqueData);
+ return !Data->Bases.count(Base);
+ }
+
+ bool mightShareBases(const CXXRecordDecl *Class) {
+ return Bases.count(Class) || !Class->forallBases(doesNotContain, this);
+ }
+ };
+
+ UserData Data;
+
+ // Returns false if we find a dependent base.
+ if (Data.hasDependentBases(cast<CXXRecordDecl>(CurContext)))
+ return false;
+
+ // Returns false if the class has a dependent base or if it or one
+ // of its bases is present in the base set of the current context.
+ if (Data.mightShareBases(cast<CXXRecordDecl>(NamedContext)))
+ return false;
+
+ Diag(SS.getRange().getBegin(),
+ diag::err_using_decl_nested_name_specifier_is_not_base_class)
+ << (NestedNameSpecifier*) SS.getScopeRep()
+ << cast<CXXRecordDecl>(CurContext)
+ << SS.getRange();
+
+ return true;
}
Sema::DeclPtrTy Sema::ActOnNamespaceAliasDef(Scope *S,
Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp?rev=90843&r1=90842&r2=90843&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateDecl.cpp Tue Dec 8 01:46:18 2009
@@ -1071,17 +1071,12 @@
cast<NamedDecl>(SemaRef.FindInstantiatedDecl(D->getTargetDecl(),
TemplateArgs));
- UsingShadowDecl *InstD = UsingShadowDecl::Create(SemaRef.Context, Owner,
- InstUsing->getLocation(),
- InstUsing, InstTarget);
- InstUsing->addShadowDecl(InstD);
-
- if (InstTarget->isInvalidDecl() || InstUsing->isInvalidDecl())
- InstD->setInvalidDecl();
+ UsingShadowDecl *InstD = SemaRef.BuildUsingShadowDecl(/*Scope*/ 0,
+ D->getAccess(),
+ InstUsing,
+ InstTarget);
SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstD, D);
- InstD->setAccess(D->getAccess());
- Owner->addDecl(InstD);
return InstD;
}
Modified: cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3-cxx0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3-cxx0x.cpp?rev=90843&r1=90842&r2=90843&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3-cxx0x.cpp (original)
+++ cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p3-cxx0x.cpp Tue Dec 8 01:46:18 2009
@@ -1,4 +1,4 @@
-// RUN: clang-cc -fsyntax-only -verify %s
+// RUN: clang-cc -std=c++0x -fsyntax-only -verify %s
// C++0x N2914.
struct B {
@@ -18,3 +18,29 @@
using B::x;
using C::g; // expected-error{{using declaration refers into 'C::', which is not a base class of 'D2'}}
};
+
+namespace test1 {
+ struct Base {
+ int foo();
+ };
+
+ struct Unrelated {
+ int foo();
+ };
+
+ struct Subclass : Base {
+ };
+
+ namespace InnerNS {
+ int foo();
+ }
+
+ // We should be able to diagnose these without instantiation.
+ template <class T> struct C : Base {
+ using InnerNS::foo; // expected-error {{not a class}}
+ using Base::bar; // expected-error {{no member named 'bar'}}
+ using Unrelated::foo; // expected-error {{not a base class}}
+ using C::foo; // expected-error {{refers to its own class}}
+ using Subclass::foo; // expected-error {{not a base class}}
+ };
+}
Modified: cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp?rev=90843&r1=90842&r2=90843&view=diff
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp (original)
+++ cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p4.cpp Tue Dec 8 01:46:18 2009
@@ -21,10 +21,10 @@
}
class Test0 {
- using NonClass::type; // expected-error {{not a base class}}
- using NonClass::hiding; // expected-error {{not a base class}}
- using NonClass::union_member; // expected-error {{not a base class}}
- using NonClass::enumerator; // expected-error {{not a base class}}
+ using NonClass::type; // expected-error {{not a class}}
+ using NonClass::hiding; // expected-error {{not a class}}
+ using NonClass::union_member; // expected-error {{not a class}}
+ using NonClass::enumerator; // expected-error {{not a class}}
};
}
@@ -181,3 +181,32 @@
template struct C<int>; // expected-note {{in instantiation}}
}
+
+namespace test4 {
+ struct Base {
+ int foo();
+ };
+
+ struct Unrelated {
+ int foo();
+ };
+
+ struct Subclass : Base {
+ };
+
+ namespace InnerNS {
+ int foo();
+ }
+
+ // We should be able to diagnose these without instantiation.
+ template <class T> struct C : Base {
+ using InnerNS::foo; // expected-error {{not a class}}
+ using Base::bar; // expected-error {{no member named 'bar'}}
+ using Unrelated::foo; // expected-error {{not a base class}}
+ using C::foo; // legal in C++03
+ using Subclass::foo; // legal in C++03
+
+ int bar(); //expected-note {{target of using declaration}}
+ using C::bar; // expected-error {{refers to its own class}}
+ };
+}
Modified: cfe/trunk/test/SemaCXX/using-decl-templates.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/using-decl-templates.cpp?rev=90843&r1=90842&r2=90843&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/using-decl-templates.cpp (original)
+++ cfe/trunk/test/SemaCXX/using-decl-templates.cpp Tue Dec 8 01:46:18 2009
@@ -10,7 +10,7 @@
using A<T>::N; // expected-error{{dependent using declaration resolved to type without 'typename'}}
using A<T>::foo; // expected-error{{no member named 'foo'}}
- using A<double>::f; // expected-error{{using declaration refers into 'A<double>::', which is not a base class of 'B'}}
+ using A<double>::f; // expected-error{{using declaration refers into 'A<double>::', which is not a base class of 'B<int>'}}
};
B<int> a; // expected-note{{in instantiation of template class 'struct B<int>' requested here}}
More information about the cfe-commits
mailing list