diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index d278dbce0787..32a52aa5250a 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -76,63 +76,24 @@ public: #include "clang/AST/DeclNodes.def" }; - /// IdentifierNamespace - The different namespaces in which - /// declarations may appear. According to C99 6.2.3, there are - /// four namespaces, labels, tags, members and ordinary - /// identifiers. C++ describes lookup completely differently: - /// certain lookups merely "ignore" certain kinds of declarations, - /// usually based on whether the declaration is of a type, etc. - /// - /// These are meant as bitmasks, so that searches in - /// C++ can look into the "tag" namespace during ordinary lookup. - /// - /// Decl currently provides 16 bits of IDNS bits. + /// IdentifierNamespace - According to C99 6.2.3, there are four + /// namespaces, labels, tags, members and ordinary + /// identifiers. These are meant as bitmasks, so that searches in + /// C++ can look into the "tag" namespace during ordinary lookup. We + /// use additional namespaces for Objective-C entities. We also put + /// C++ friend declarations (of previously-undeclared entities) in + /// shadow namespaces, and 'using' declarations (as opposed to their + /// implicit shadow declarations) can be found in their own + /// namespace. enum IdentifierNamespace { - /// Labels, declared with 'x:' and referenced with 'goto x'. - IDNS_Label = 0x0001, - - /// Tags, declared with 'struct foo;' and referenced with - /// 'struct foo'. All tags are also types. This is what - /// elaborated-type-specifiers look for in C. - IDNS_Tag = 0x0002, - - /// Types, declared with 'struct foo', typedefs, etc. - /// This is what elaborated-type-specifiers look for in C++, - /// but note that it's ill-formed to find a non-tag. - IDNS_Type = 0x0004, - - /// Members, declared with object declarations within tag - /// definitions. In C, these can only be found by "qualified" - /// lookup in member expressions. In C++, they're found by - /// normal lookup. - IDNS_Member = 0x0008, - - /// Namespaces, declared with 'namespace foo {}'. - /// Lookup for nested-name-specifiers find these. - IDNS_Namespace = 0x0010, - - /// Ordinary names. In C, everything that's not a label, tag, - /// or member ends up here. - IDNS_Ordinary = 0x0020, - - /// Objective C @protocol. - IDNS_ObjCProtocol = 0x0040, - - /// This declaration is a friend function. A friend function - /// declaration is always in this namespace but may also be in - /// IDNS_Ordinary if it was previously declared. - IDNS_OrdinaryFriend = 0x0080, - - /// This declaration is a friend class. A friend class - /// declaration is always in this namespace but may also be in - /// IDNS_Tag|IDNS_Type if it was previously declared. - IDNS_TagFriend = 0x0100, - - /// This declaration is a using declaration. A using declaration - /// *introduces* a number of other declarations into the current - /// scope, and those declarations use the IDNS of their targets, - /// but the actual using declarations go in this namespace. - IDNS_Using = 0x0200 + IDNS_Label = 0x1, + IDNS_Tag = 0x2, + IDNS_Member = 0x4, + IDNS_Ordinary = 0x8, + IDNS_ObjCProtocol = 0x10, + IDNS_OrdinaryFriend = 0x80, + IDNS_TagFriend = 0x100, + IDNS_Using = 0x200 }; /// ObjCDeclQualifier - Qualifier used on types in method declarations @@ -492,14 +453,14 @@ public: assert((OldNS & (IDNS_Tag | IDNS_Ordinary | IDNS_TagFriend | IDNS_OrdinaryFriend)) && "namespace includes neither ordinary nor tag"); - assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary | IDNS_Type | + assert(!(OldNS & ~(IDNS_Tag | IDNS_Ordinary | IDNS_TagFriend | IDNS_OrdinaryFriend)) && "namespace includes other than ordinary or tag"); IdentifierNamespace = 0; if (OldNS & (IDNS_Tag | IDNS_TagFriend)) { IdentifierNamespace |= IDNS_TagFriend; - if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag | IDNS_Type; + if (PreviouslyDeclared) IdentifierNamespace |= IDNS_Tag; } if (OldNS & (IDNS_Ordinary | IDNS_OrdinaryFriend)) { diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 32feee09a8f1..16de2012c236 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1583,11 +1583,6 @@ def err_redefinition_different_kind : Error< "redefinition of %0 as different kind of symbol">; def err_redefinition_different_typedef : Error< "typedef redefinition with different types (%0 vs %1)">; -def err_tag_reference_non_tag : Error< - "elaborated type refers to %select{a non-tag type|a typedef|a template}0">; -def err_tag_reference_conflict : Error< - "implicit declaration introduced by elaborated type conflicts with " - "%select{a declaration|a typedef|a template}0 of the same name">; def err_tag_definition_of_typedef : Error< "definition of type %0 conflicts with typedef of the same name">; def err_conflicting_types : Error<"conflicting types for %0">; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index a97536cc77e2..d3268030c91b 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1438,7 +1438,7 @@ Decl *ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) { for (DeclContext::lookup_result Lookup = DC->lookup(Name); Lookup.first != Lookup.second; ++Lookup.first) { - if (!(*Lookup.first)->isInIdentifierNamespace(Decl::IDNS_Namespace)) + if (!(*Lookup.first)->isInIdentifierNamespace(Decl::IDNS_Ordinary)) continue; if (NamespaceDecl *FoundNS = dyn_cast(*Lookup.first)) { @@ -1451,7 +1451,7 @@ Decl *ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) { } if (!ConflictingDecls.empty()) { - Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Namespace, + Name = Importer.HandleNameConflict(Name, DC, Decl::IDNS_Ordinary, ConflictingDecls.data(), ConflictingDecls.size()); } diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index b5aec0c5125c..61d22b9d7075 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -231,28 +231,23 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case CXXConstructor: case CXXDestructor: case CXXConversion: + case Typedef: case EnumConstant: case Var: case ImplicitParam: case ParmVar: case NonTypeTemplateParm: case ObjCMethod: - case ObjCProperty: - return IDNS_Ordinary; - - case ObjCCompatibleAlias: case ObjCInterface: - return IDNS_Ordinary | IDNS_Type; - - case Typedef: - case UnresolvedUsingTypename: - case TemplateTypeParm: - return IDNS_Ordinary | IDNS_Type; + case ObjCProperty: + case ObjCCompatibleAlias: + return IDNS_Ordinary; case UsingShadow: return 0; // we'll actually overwrite this later case UnresolvedUsingValue: + case UnresolvedUsingTypename: return IDNS_Ordinary | IDNS_Using; case Using: @@ -269,18 +264,15 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case Record: case CXXRecord: case Enum: - return IDNS_Tag | IDNS_Type; + case TemplateTypeParm: + return IDNS_Tag; case Namespace: - case NamespaceAlias: - return IDNS_Namespace; - case FunctionTemplate: - return IDNS_Ordinary; - case ClassTemplate: case TemplateTemplateParm: - return IDNS_Ordinary | IDNS_Tag | IDNS_Type; + case NamespaceAlias: + return IDNS_Tag | IDNS_Ordinary; // Never have names. case Friend: diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 8be3da06ac42..e71f8c84ebc5 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -415,9 +415,6 @@ bool ResultBuilder::isInterestingDecl(NamedDecl *ND, return false; } - - if (Filter == &ResultBuilder::IsNestedNameSpecifier) - AsNestedNameSpecifier = true; // ... then it must be interesting! return true; @@ -507,8 +504,7 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) { } for (; I != IEnd; ++I) { // A tag declaration does not hide a non-tag declaration. - if (I->first->getIdentifierNamespace() - == (Decl::IDNS_Tag | Decl::IDNS_Type) && + if (I->first->getIdentifierNamespace() == Decl::IDNS_Tag && (IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary | Decl::IDNS_ObjCProtocol))) continue; @@ -633,7 +629,7 @@ void ResultBuilder::ExitScope() { bool ResultBuilder::IsOrdinaryName(NamedDecl *ND) const { unsigned IDNS = Decl::IDNS_Ordinary; if (SemaRef.getLangOptions().CPlusPlus) - IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace; + IDNS |= Decl::IDNS_Tag; else if (SemaRef.getLangOptions().ObjC1 && isa(ND)) return true; @@ -645,7 +641,7 @@ bool ResultBuilder::IsOrdinaryName(NamedDecl *ND) const { bool ResultBuilder::IsOrdinaryNonValueName(NamedDecl *ND) const { unsigned IDNS = Decl::IDNS_Ordinary; if (SemaRef.getLangOptions().CPlusPlus) - IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace; + IDNS |= Decl::IDNS_Tag; return (ND->getIdentifierNamespace() & IDNS) && !isa(ND) && !isa(ND); @@ -2098,16 +2094,10 @@ void Sema::CodeCompleteTag(Scope *S, unsigned TagSpec) { return; } - ResultBuilder Results(*this); + ResultBuilder Results(*this, Filter); + Results.allowNestedNameSpecifiers(); CodeCompletionDeclConsumer Consumer(Results, CurContext); - - // First pass: look for tags. - Results.setFilter(Filter); LookupVisibleDecls(S, LookupTagName, Consumer); - - // Second pass: look for nested name specifiers. - Results.setFilter(&ResultBuilder::IsNestedNameSpecifier); - LookupVisibleDecls(S, LookupNestedNameSpecifierName, Consumer); HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e0ca252b0611..c9001f10303a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5000,8 +5000,7 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SearchDC = SearchDC->getEnclosingNamespaceContext(); } - // In C++, we need to do a redeclaration lookup to properly - // diagnose some problems. + // In C++, look for a shadow friend decl. if (getLangOptions().CPlusPlus) { Previous.setRedeclarationKind(ForRedeclaration); LookupQualifiedName(Previous, SearchDC); @@ -5010,26 +5009,6 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, if (!Previous.empty()) { NamedDecl *PrevDecl = (*Previous.begin())->getUnderlyingDecl(); - - // It's okay to have a tag decl in the same scope as a typedef - // which shadows a tag decl in the same scope. Finding this - // insanity with a redeclaration lookup can only actually happen - // in C++. - if (getLangOptions().CPlusPlus && - TUK != TUK_Reference && TUK != TUK_Friend) { - if (TypedefDecl *TD = dyn_cast(PrevDecl)) { - if (const TagType *TT = TD->getUnderlyingType()->getAs()) { - TagDecl *Tag = TT->getDecl(); - if (Tag->getDeclName() == Name && - Tag->getDeclContext()->Equals(SearchDC)) { - PrevDecl = Tag; - Previous.clear(); - Previous.addDecl(Tag); - } - } - } - } - if (TagDecl *PrevTagDecl = dyn_cast(PrevDecl)) { // If this is a use of a previous tag, or if the tag is already declared // in the same scope (so that the definition/declaration completes or @@ -5121,61 +5100,23 @@ Sema::DeclPtrTy Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // If we get here, we're going to create a new Decl. If PrevDecl // is non-NULL, it's a definition of the tag declared by // PrevDecl. If it's NULL, we have a new definition. - - - // Otherwise, PrevDecl is not a tag, but was found with tag - // lookup. This is only actually possible in C++, where a few - // things like templates still live in the tag namespace. } else { - assert(getLangOptions().CPlusPlus); - - // Use a better diagnostic if an elaborated-type-specifier - // found the wrong kind of type on the first - // (non-redeclaration) lookup. - if ((TUK == TUK_Reference || TUK == TUK_Friend) && - !Previous.isForRedeclaration()) { - unsigned Kind = 0; - if (isa(PrevDecl)) Kind = 1; - else if (isa(PrevDecl)) Kind = 2; - Diag(NameLoc, diag::err_tag_reference_non_tag) << Kind; - Diag(PrevDecl->getLocation(), diag::note_declared_at); - Invalid = true; - - // Otherwise, only diagnose if the declaration is in scope. - } else if (!isDeclInScope(PrevDecl, SearchDC, S)) { - // do nothing - - // Diagnose implicit declarations introduced by elaborated types. - } else if (TUK == TUK_Reference || TUK == TUK_Friend) { - unsigned Kind = 0; - if (isa(PrevDecl)) Kind = 1; - else if (isa(PrevDecl)) Kind = 2; - Diag(NameLoc, diag::err_tag_reference_conflict) << Kind; - Diag(PrevDecl->getLocation(), diag::note_previous_decl) << PrevDecl; - Invalid = true; - - // Otherwise it's a declaration. Call out a particularly common - // case here. - } else if (isa(PrevDecl)) { - Diag(NameLoc, diag::err_tag_definition_of_typedef) - << Name - << cast(PrevDecl)->getUnderlyingType(); - Diag(PrevDecl->getLocation(), diag::note_previous_decl) << PrevDecl; - Invalid = true; - - // Otherwise, diagnose. - } else { - // The tag name clashes with something else in the target scope, - // issue an error and recover by making this tag be anonymous. + // PrevDecl is a namespace, template, or anything else + // that lives in the IDNS_Tag identifier namespace. + if (TUK == TUK_Reference || TUK == TUK_Friend || + isDeclInScope(PrevDecl, SearchDC, S)) { + // The tag name clashes with a namespace name, issue an error and + // recover by making this tag be anonymous. Diag(NameLoc, diag::err_redefinition_different_kind) << Name; Diag(PrevDecl->getLocation(), diag::note_previous_definition); Name = 0; + Previous.clear(); Invalid = true; + } else { + // The existing declaration isn't relevant to us; we're in a + // new scope, so clear out the previous declaration. + Previous.clear(); } - - // The existing declaration isn't relevant to us; we're in a - // new scope, so clear out the previous declaration. - Previous.clear(); } } @@ -5246,6 +5187,28 @@ CreateNewDecl: New->addAttr(::new (Context) PragmaPackAttr(Alignment * 8)); } + if (getLangOptions().CPlusPlus && SS.isEmpty() && Name && !Invalid) { + // C++ [dcl.typedef]p3: + // [...] Similarly, in a given scope, a class or enumeration + // shall not be declared with the same name as a typedef-name + // that is declared in that scope and refers to a type other + // than the class or enumeration itself. + LookupResult Lookup(*this, Name, NameLoc, LookupOrdinaryName, + ForRedeclaration); + LookupName(Lookup, S); + TypedefDecl *PrevTypedef = Lookup.getAsSingle(); + NamedDecl *PrevTypedefNamed = PrevTypedef; + if (PrevTypedef && isDeclInScope(PrevTypedefNamed, SearchDC, S) && + Context.getCanonicalType(Context.getTypeDeclType(PrevTypedef)) != + Context.getCanonicalType(Context.getTypeDeclType(New))) { + Diag(Loc, diag::err_tag_definition_of_typedef) + << Context.getTypeDeclType(New) + << PrevTypedef->getUnderlyingType(); + Diag(PrevTypedef->getLocation(), diag::note_previous_definition); + Invalid = true; + } + } + // If this is a specialization of a member class (of a class template), // check the specialization. if (isExplicitSpecialization && CheckMemberSpecialization(New, Previous)) diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 445d08e6669a..8de0c40fb767 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -202,6 +202,26 @@ static bool IsAcceptableOperatorName(NamedDecl *D, unsigned IDNS) { !D->getDeclContext()->isRecord(); } +static bool IsAcceptableNestedNameSpecifierName(NamedDecl *D, unsigned IDNS) { + // This lookup ignores everything that isn't a type. + + // This is a fast check for the far most common case. + if (D->isInIdentifierNamespace(Decl::IDNS_Tag)) + return true; + + if (isa(D)) + D = cast(D)->getTargetDecl(); + + return isa(D); +} + +static bool IsAcceptableNamespaceName(NamedDecl *D, unsigned IDNS) { + // We don't need to look through using decls here because + // using decls aren't allowed to name namespaces. + + return isa(D) || isa(D); +} + /// Gets the default result filter for the given lookup. static inline LookupResult::ResultFilter getResultFilter(Sema::LookupNameKind NameKind) { @@ -212,12 +232,16 @@ LookupResult::ResultFilter getResultFilter(Sema::LookupNameKind NameKind) { case Sema::LookupRedeclarationWithLinkage: // FIXME: check linkage, scoping case Sema::LookupUsingDeclName: case Sema::LookupObjCProtocolName: - case Sema::LookupNestedNameSpecifierName: - case Sema::LookupNamespaceName: return &IsAcceptableIDNS; case Sema::LookupOperatorName: return &IsAcceptableOperatorName; + + case Sema::LookupNestedNameSpecifierName: + return &IsAcceptableNestedNameSpecifierName; + + case Sema::LookupNamespaceName: + return &IsAcceptableNamespaceName; } llvm_unreachable("unkknown lookup kind"); @@ -236,25 +260,15 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind, case Sema::LookupRedeclarationWithLinkage: IDNS = Decl::IDNS_Ordinary; if (CPlusPlus) { - IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Namespace; + IDNS |= Decl::IDNS_Tag | Decl::IDNS_Member; if (Redeclaration) IDNS |= Decl::IDNS_TagFriend | Decl::IDNS_OrdinaryFriend; } break; case Sema::LookupTagName: - if (CPlusPlus) { - IDNS = Decl::IDNS_Type; - - // When looking for a redeclaration of a tag name, we add: - // 1) TagFriend to find undeclared friend decls - // 2) Namespace because they can't "overload" with tag decls. - // 3) Tag because it includes class templates, which can't - // "overload" with tag decls. - if (Redeclaration) - IDNS |= Decl::IDNS_Tag | Decl::IDNS_TagFriend | Decl::IDNS_Namespace; - } else { - IDNS = Decl::IDNS_Tag; - } + IDNS = Decl::IDNS_Tag; + if (CPlusPlus && Redeclaration) + IDNS |= Decl::IDNS_TagFriend; break; case Sema::LookupMemberName: @@ -264,11 +278,8 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind, break; case Sema::LookupNestedNameSpecifierName: - IDNS = Decl::IDNS_Type | Decl::IDNS_Namespace; - break; - case Sema::LookupNamespaceName: - IDNS = Decl::IDNS_Namespace; + IDNS = Decl::IDNS_Ordinary | Decl::IDNS_Tag | Decl::IDNS_Member; break; case Sema::LookupUsingDeclName: @@ -2123,8 +2134,7 @@ NamedDecl *VisibleDeclsRecord::checkHidden(NamedDecl *ND) { IEnd = Pos->second.end(); I != IEnd; ++I) { // A tag declaration does not hide a non-tag declaration. - if ((*I)->getIdentifierNamespace() - == (Decl::IDNS_Tag|Decl::IDNS_Type) && + if ((*I)->getIdentifierNamespace() == Decl::IDNS_Tag && (IDNS & (Decl::IDNS_Member | Decl::IDNS_Ordinary | Decl::IDNS_ObjCProtocol))) continue; diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.elab/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.elab/p2.cpp deleted file mode 100644 index 0e0c45595ca4..000000000000 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.elab/p2.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s - -namespace test0 { - struct A { - static int foo; - }; - - namespace i0 { - typedef int A; // expected-note {{declared here}} - - int test() { - struct A a; // expected-error {{elaborated type refers to a typedef}} - return a.foo; - } - } - - namespace i1 { - template class A; // expected-note {{declared here}} - - int test() { - struct A a; // expected-error {{elaborated type refers to a template}} - return a.foo; - } - } - - namespace i2 { - int A; - - int test() { - struct A a; - return a.foo; - } - } - - namespace i3 { - void A(); - - int test() { - struct A a; - return a.foo; - } - } - - namespace i4 { - template void A(); - - int test() { - struct A a; - return a.foo; - } - } -} diff --git a/clang/test/CXX/class.access/class.friend/p1.cpp b/clang/test/CXX/class.access/class.friend/p1.cpp index 991698d5dcca..91d7661be3f7 100644 --- a/clang/test/CXX/class.access/class.friend/p1.cpp +++ b/clang/test/CXX/class.access/class.friend/p1.cpp @@ -280,10 +280,3 @@ namespace test8 { } template A::I g2(A::I i); } - -// PR6885 -namespace test9 { - class B { - friend class test9; - }; -} diff --git a/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp index 073b2a146354..7604a23edb08 100644 --- a/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -155,7 +155,7 @@ namespace Dependent { } namespace test7 { - template class A { // expected-note {{declared here}} + template class A { // expected-note {{previous definition is here}} friend class B; int x; // expected-note {{declared private here}} }; @@ -174,7 +174,7 @@ namespace test7 { // This shouldn't crash. template class D { - friend class A; // expected-error {{elaborated type refers to a template}} + friend class A; // expected-error {{redefinition of 'A' as different kind of symbol}} }; template class D; } diff --git a/clang/test/SemaCXX/typedef-redecl.cpp b/clang/test/SemaCXX/typedef-redecl.cpp index 2acf6757fa3d..c382539e05f1 100644 --- a/clang/test/SemaCXX/typedef-redecl.cpp +++ b/clang/test/SemaCXX/typedef-redecl.cpp @@ -13,7 +13,7 @@ struct X { struct Y; // expected-note{{previous definition is here}} typedef int Y; // expected-error{{typedef redefinition with different types ('int' vs 'Y')}} -typedef int Y2; // expected-note{{declared here}} +typedef int Y2; // expected-note{{previous definition is here}} struct Y2; // expected-error{{definition of type 'Y2' conflicts with typedef of the same name}} void f(); // expected-note{{previous definition is here}}