From b8c419085f7dbcabb3ba7efa59859e78bd62952c Mon Sep 17 00:00:00 2001 From: Richard Smith 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. llvm-svn: 312743 --- clang/include/clang/AST/DeclBase.h | 10 ++++++---- clang/lib/AST/DeclBase.cpp | 11 +++++------ clang/lib/Sema/SemaDecl.cpp | 18 ++++++++---------- .../basic.scope.declarative/p4.cpp | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 clang/test/CXX/basic/basic.scope/basic.scope.declarative/p4.cpp diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index c74879d8e399..fe9b96ffe1e9 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/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/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index abe17b5a8404..42f0ba0bd796 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/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/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index d6de97feb080..36317c0e7dfd 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/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/clang/test/CXX/basic/basic.scope/basic.scope.declarative/p4.cpp b/clang/test/CXX/basic/basic.scope/basic.scope.declarative/p4.cpp new file mode 100644 index 000000000000..8c14546edc74 --- /dev/null +++ b/clang/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 int vartemplate_a; // expected-error {{redefinition}} + template int vartemplate_b; // expected-note {{previous}} + struct vartemplate_b {}; // expected-error {{redefinition}} + + struct aliastemplate_a {}; // expected-note {{previous}} + template using aliastemplate_a = int; // expected-error {{redefinition}} + template using aliastemplate_b = int; // expected-note {{previous}} + struct aliastemplate_b {}; // expected-error {{redefinition}} +}