[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