From 5c19ffded186077869540ffcdc09ab0bd263d8a9 Mon Sep 17 00:00:00 2001
From: Richard Smith <richard-llvm@metafoo.co.uk>
Date: Thu, 7 Sep 2017 20:22:00 +0000
Subject: [PATCH] Add IDNS_Tag to C++ declarations that conflict with tag
 declarations.

Fixes some accepts-invalids with tags and other declarations declared in the
same scope.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@312743 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/AST/DeclBase.h                  | 10 ++++++----
 lib/AST/DeclBase.cpp                          | 11 +++++------
 lib/Sema/SemaDecl.cpp                         | 18 ++++++++----------
 .../basic.scope.declarative/p4.cpp            | 19 +++++++++++++++++++
 4 files changed, 38 insertions(+), 20 deletions(-)
 create mode 100644 test/CXX/basic/basic.scope/basic.scope.declarative/p4.cpp

diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h
index c74879d8e39..fe9b96ffe1e 100644
--- a/include/clang/AST/DeclBase.h
+++ b/include/clang/AST/DeclBase.h
@@ -1002,13 +1002,15 @@ public:
   /// declaration, but in the semantic context of the enclosing namespace
   /// scope.
   void setLocalExternDecl() {
-    assert((IdentifierNamespace == IDNS_Ordinary ||
-            IdentifierNamespace == IDNS_OrdinaryFriend) &&
-           "namespace is not ordinary");
-
     Decl *Prev = getPreviousDecl();
     IdentifierNamespace &= ~IDNS_Ordinary;
 
+    // It's OK for the declaration to still have the "invisible friend" flag or
+    // the "conflicts with tag declarations in this scope" flag for the outer
+    // scope.
+    assert((IdentifierNamespace & ~(IDNS_OrdinaryFriend | IDNS_Tag)) == 0 &&
+           "namespace is not ordinary");
+
     IdentifierNamespace |= IDNS_LocalExtern;
     if (Prev && Prev->getIdentifierNamespace() & IDNS_Ordinary)
       IdentifierNamespace |= IDNS_Ordinary;
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index abe17b5a840..42f0ba0bd79 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -681,7 +681,6 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
     case CXXConversion:
     case EnumConstant:
     case Var:
-    case Binding:
     case ImplicitParam:
     case ParmVar:
     case ObjCMethod:
@@ -693,10 +692,11 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
     case IndirectField:
       return IDNS_Ordinary | IDNS_Member;
 
+    case Binding:
     case NonTypeTemplateParm:
-      // Non-type template parameters are not found by lookups that ignore
-      // non-types, but they are found by redeclaration lookups for tag types,
-      // so we include them in the tag namespace.
+    case VarTemplate:
+      // These (C++-only) declarations are found by redeclaration lookup for
+      // tag types, so we include them in the tag namespace.
       return IDNS_Ordinary | IDNS_Tag;
 
     case ObjCCompatibleAlias:
@@ -705,7 +705,6 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
 
     case Typedef:
     case TypeAlias:
-    case TypeAliasTemplate:
     case TemplateTypeParm:
     case ObjCTypeParam:
       return IDNS_Ordinary | IDNS_Type;
@@ -741,11 +740,11 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
       return IDNS_Namespace;
 
     case FunctionTemplate:
-    case VarTemplate:
       return IDNS_Ordinary;
 
     case ClassTemplate:
     case TemplateTemplateParm:
+    case TypeAliasTemplate:
       return IDNS_Ordinary | IDNS_Tag | IDNS_Type;
 
     case OMPDeclareReduction:
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index d6de97feb08..36317c0e7df 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -5288,13 +5288,6 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
   TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S);
   QualType R = TInfo->getType();
 
-  if (!R->isFunctionType() && DiagnoseClassNameShadow(DC, NameInfo))
-    // If this is a typedef, we'll end up spewing multiple diagnostics.
-    // Just return early; it's safer. If this is a function, let the
-    // "constructor cannot have a return type" diagnostic handle it.
-    if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef)
-      return nullptr;
-
   if (DiagnoseUnexpandedParameterPack(D.getIdentifierLoc(), TInfo,
                                       UPPC_DeclarationType))
     D.setInvalidType();
@@ -5373,12 +5366,17 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
     Previous.clear();
   }
 
+  if (!R->isFunctionType() && DiagnoseClassNameShadow(DC, NameInfo))
+    // Forget that the previous declaration is the injected-class-name.
+    Previous.clear();
+
   // In C++, the previous declaration we find might be a tag type
   // (class or enum). In this case, the new declaration will hide the
-  // tag type. Note that this does does not apply if we're declaring a
-  // typedef (C++ [dcl.typedef]p4).
+  // tag type. Note that this applies to functions, function templates, and
+  // variables, but not to typedefs (C++ [dcl.typedef]p4) or variable templates.
   if (Previous.isSingleTagDecl() &&
-      D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef)
+      D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
+      (TemplateParamLists.size() == 0 || R->isFunctionType()))
     Previous.clear();
 
   // Check that there are no default arguments other than in the parameters
diff --git a/test/CXX/basic/basic.scope/basic.scope.declarative/p4.cpp b/test/CXX/basic/basic.scope/basic.scope.declarative/p4.cpp
new file mode 100644
index 00000000000..8c14546edc7
--- /dev/null
+++ b/test/CXX/basic/basic.scope/basic.scope.declarative/p4.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+namespace TagVs {
+  struct Bindable { int a; };
+  struct binding_a {}; // expected-note {{previous}}
+  auto [binding_a] = Bindable{}; // expected-error {{redefinition}}
+  auto [binding_b] = Bindable{}; // expected-note {{previous}}
+  struct binding_b {}; // expected-error {{redefinition}}
+
+  struct vartemplate_a {}; // expected-note {{previous}}
+  template<typename T> int vartemplate_a; // expected-error {{redefinition}}
+  template<typename T> int vartemplate_b; // expected-note {{previous}}
+  struct vartemplate_b {}; // expected-error {{redefinition}}
+
+  struct aliastemplate_a {}; // expected-note {{previous}}
+  template<typename T> using aliastemplate_a = int; // expected-error {{redefinition}}
+  template<typename T> using aliastemplate_b = int; // expected-note {{previous}}
+  struct aliastemplate_b {}; // expected-error {{redefinition}}
+}
-- 
GitLab