From: Douglas Gregor Date: Wed, 28 Jan 2009 17:15:10 +0000 (+0000) Subject: Complete semantic checking for typedef redeclarations in C++. The X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=66973121788ca645fe3d4a66179b9cfb6f2bce08;p=clang Complete semantic checking for typedef redeclarations in C++. The rules are slightly different than in C, and now we handle both dialects properly. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63211 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index f73f6ee23f..6cc23dd427 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -468,6 +468,8 @@ DIAG(err_redefinition_different_kind, ERROR, "redefinition of %0 as different kind of symbol") DIAG(err_redefinition_different_typedef, ERROR, "typedef redefinition with different types (%0 vs %1)") +DIAG(err_tag_definition_of_typedef, ERROR, + "definition of type %0 conflicts with typedef of the same name") DIAG(err_conflicting_types, ERROR, "conflicting types for %0") DIAG(err_nested_redefinition, ERROR, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e0cb526c60..22650f525d 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -384,23 +384,31 @@ TypedefDecl *Sema::MergeTypeDefDecl(TypedefDecl *New, Decl *OldD) { } // Fall through - the typedef name was not a builtin type. } - // Verify the old decl was also a typedef. - TypedefDecl *Old = dyn_cast(OldD); + // Verify the old decl was also a type. + TypeDecl *Old = dyn_cast(OldD); if (!Old) { - Diag(New->getLocation(), diag::err_redefinition_different_kind) + Diag(New->getLocation(), diag::err_redefinition_different_kind) << New->getDeclName(); if (!objc_types) Diag(OldD->getLocation(), diag::note_previous_definition); return New; } - + + // Determine the "old" type we'll use for checking and diagnostics. + QualType OldType; + if (TypedefDecl *OldTypedef = dyn_cast(Old)) + OldType = OldTypedef->getUnderlyingType(); + else + OldType = Context.getTypeDeclType(Old); + // If the typedef types are not identical, reject them in all languages and // with any extensions enabled. - if (Old->getUnderlyingType() != New->getUnderlyingType() && - Context.getCanonicalType(Old->getUnderlyingType()) != + + if (OldType != New->getUnderlyingType() && + Context.getCanonicalType(OldType) != Context.getCanonicalType(New->getUnderlyingType())) { Diag(New->getLocation(), diag::err_redefinition_different_typedef) - << New->getUnderlyingType() << Old->getUnderlyingType(); + << New->getUnderlyingType() << OldType; if (!objc_types) Diag(Old->getLocation(), diag::note_previous_definition); return New; @@ -1276,8 +1284,10 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, // 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. - if (PrevDecl && PrevDecl->getIdentifierNamespace() == Decl::IDNS_Tag) + // tag type. Note that this does does not apply if we're declaring a + // typedef (C++ [dcl.typedef]p4). + if (PrevDecl && PrevDecl->getIdentifierNamespace() == Decl::IDNS_Tag && + D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef) PrevDecl = 0; QualType R = GetTypeForDeclarator(D, S); @@ -2887,8 +2897,6 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, } if (PrevDecl) { - assert((isa(PrevDecl) || isa(PrevDecl)) && - "unexpected Decl type"); 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 @@ -2955,7 +2963,8 @@ Sema::DeclTy *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagKind TK, // is non-NULL, it's a definition of the tag declared by // PrevDecl. If it's NULL, we have a new definition. } else { - // PrevDecl is a namespace. + // PrevDecl is a namespace, template, or anything else + // that lives in the IDNS_Tag identifier namespace. if (isDeclInScope(PrevDecl, SearchDC, S)) { // The tag name clashes with a namespace name, issue an error and // recover by making this tag be anonymous. @@ -3054,6 +3063,30 @@ CreateNewDecl: New->addAttr(new PackedAttr(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 = LookupName(S, Name, + LookupCriteria(LookupCriteria::Ordinary, + true, true)); + TypedefDecl *PrevTypedef = 0; + if (Lookup.getKind() == LookupResult::Found) + PrevTypedef = dyn_cast(Lookup.getAsDecl()); + + if (PrevTypedef && isDeclInScope(PrevTypedef, 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 (Invalid) New->setInvalidDecl(); diff --git a/test/SemaCXX/class.cpp b/test/SemaCXX/class.cpp index d739af87dd..02608faa0f 100644 --- a/test/SemaCXX/class.cpp +++ b/test/SemaCXX/class.cpp @@ -30,7 +30,7 @@ public: func btm : 1; // expected-error {{error: bit-field 'btm' with non-integral type}} NestedC bc : 1; // expected-error {{error: bit-field 'bc' with non-integral type}} - enum E { en1, en2 }; + enum E1 { en1, en2 }; int i = 0; // expected-error {{error: 'i' can only be initialized if it is a static const integral data member}} static int si = 0; // expected-error {{error: 'si' can only be initialized if it is a static const integral data member}} diff --git a/test/SemaCXX/typedef-redecl.cpp b/test/SemaCXX/typedef-redecl.cpp index eabcef8b31..016882feb2 100644 --- a/test/SemaCXX/typedef-redecl.cpp +++ b/test/SemaCXX/typedef-redecl.cpp @@ -1,12 +1,33 @@ // RUN: clang -fsyntax-only -verify %s - typedef int INT; typedef INT REALLY_INT; // expected-note {{previous definition is here}} typedef REALLY_INT REALLY_REALLY_INT; typedef REALLY_INT BOB; typedef float REALLY_INT; // expected-error{{typedef redefinition with different types ('float' vs 'INT')}} -class X { +struct X { typedef int result_type; // expected-note {{previous definition is here}} typedef INT result_type; // expected-error {{redefinition of 'result_type'}} }; + +struct Y; // expected-note{{previous definition is here}} +typedef int Y; // expected-error{{typedef redefinition with different types ('int' vs 'struct Y')}} + +typedef int Y2; // expected-note{{previous definition is here}} +struct Y2; // expected-error{{definition of type 'struct Y2' conflicts with typedef of the same name}} + +void f(); // expected-note{{previous definition is here}} +typedef int f; // expected-error{{redefinition of 'f' as different kind of symbol}} + +typedef int f2; // expected-note{{previous definition is here}} +void f2(); // expected-error{{redefinition of 'f2' as different kind of symbol}} + +typedef struct s s; +typedef int I; +typedef int I; +typedef I I; + +struct s { }; + +typedef class st { /* ... */ } st; // expected-note{{previous use is here}} +struct st; // expected-error{{use of 'st' with tag type that does not match previous declaration}} diff --git a/www/cxx_status.html b/www/cxx_status.html index a83157b193..22a419af5e 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -944,13 +944,20 @@ welcome!

✓ ✓ - I think we handle everything. + 7 [dcl.dcl]   7.1 [dcl.spec]     7.1.1 [dcl.stc]     7.1.2 [dcl.fct.spec] -    7.1.3 [dcl.typedef] + +     7.1.3 [dcl.typedef] + ✓ + ✓ + ✓ + + Typedefs of anonymous tag types do not use the name of the typedef for linkage purposes. +     7.1.4 [dcl.friend]     7.1.5 [dcl.type]       7.1.5.1 [dcl.type.cv]