diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-01-07 19:46:03 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-01-07 19:46:03 +0000 |
commit | 6b3945f4bc757bdadd3e443180cf32c2cccb52a0 (patch) | |
tree | f5c67895a9e956ac2d92e869e8a4c5e28e2bb14d | |
parent | 39cbfaadbcd0008492fc1ea967b6cc1301a938a1 (diff) |
Finished semantic analysis of anonymous unions in C++.
Duplicate-member checking within classes is still a little messy, and
anonymous unions are still completely broken in C. We'll need to unify
the handling of fields in C and C++ to make this code applicable in
both languages.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61878 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/Decl.h | 21 | ||||
-rw-r--r-- | include/clang/AST/DeclBase.h | 10 | ||||
-rw-r--r-- | include/clang/AST/DeclCXX.h | 42 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticKinds.def | 12 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 9 | ||||
-rw-r--r-- | lib/AST/DeclCXX.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 103 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 13 | ||||
-rw-r--r-- | test/SemaCXX/anonymous-union.cpp | 19 | ||||
-rw-r--r-- | www/cxx_status.html | 8 |
10 files changed, 170 insertions, 69 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index ceea987ce0..460e3de228 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -160,6 +160,9 @@ public: const_cast<const ScopedDecl*>(this)->getDeclContext()); } + void setAccess(AccessSpecifier AS) { Access = AS; } + AccessSpecifier getAccess() const { return AccessSpecifier(Access); } + /// getLexicalDeclContext - The declaration context where this ScopedDecl was /// lexically declared (LexicalDC). May be different from /// getDeclContext() (SemanticDC). @@ -360,7 +363,7 @@ public: StorageClass getStorageClass() const { return (StorageClass)SClass; } SourceLocation getTypeSpecStartLoc() const { return TypeSpecStartLoc; } - + const Expr *getInit() const { return (const Expr*) Init; } Expr *getInit() { return (Expr*) Init; } void setInit(Expr *I) { Init = (Stmt*) I; } @@ -624,7 +627,6 @@ private: // NOTE: VC++ treats enums as signed, avoid using the StorageClass enum unsigned SClass : 2; bool IsInline : 1; - bool IsImplicit : 1; // Move to DeclGroup when it is implemented. SourceLocation TypeSpecStartLoc; @@ -636,7 +638,7 @@ protected: : ValueDecl(DK, DC, L, N, T, PrevDecl), DeclContext(DK), ParamInfo(0), Body(0), PreviousDeclaration(0), - SClass(S), IsInline(isInline), IsImplicit(0), TypeSpecStartLoc(TSSL) {} + SClass(S), IsInline(isInline), TypeSpecStartLoc(TSSL) {} virtual ~FunctionDecl(); virtual void Destroy(ASTContext& C); @@ -670,9 +672,6 @@ public: void setBody(Stmt *B) { Body = B; } - bool isImplicit() { return IsImplicit; } - void setImplicit() { IsImplicit = true; } - /// getPreviousDeclaration - Return the previous declaration of this /// function. const FunctionDecl *getPreviousDeclaration() const { @@ -776,6 +775,12 @@ public: /// isBitfield - Determines whether this field is a bitfield. bool isBitField() const { return BitWidth != NULL; } + /// isAnonymousStructOrUnion - Determines whether this field is a + /// representative for an anonymous struct or union. Such fields are + /// unnamed and are implicitly generated by the implementation to + /// store the data for the anonymous union or struct. + bool isAnonymousStructOrUnion() const; + Expr *getBitWidth() const { return BitWidth; } // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { @@ -852,10 +857,8 @@ protected: TypeDecl(Kind DK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, ScopedDecl *PrevDecl) : ScopedDecl(DK, DC, L, Id, PrevDecl), TypeForDecl(0) {} -public: - void setAccess(AccessSpecifier AS) { Access = AS; } - AccessSpecifier getAccess() const { return AccessSpecifier(Access); } +public: // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= TypeFirst && D->getKind() <= TypeLast; diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index cc60d624f6..482bf12b46 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -141,6 +141,10 @@ private: /// HasAttrs - This indicates whether the decl has attributes or not. unsigned int HasAttrs : 1; + /// Implicit - Whether this declaration was implicitly generated by + /// the implementation rather than explicitly written by the user. + bool Implicit : 1; + protected: /// Access - Used by C++ decls for the access specifier. // NOTE: VC++ treats enums as signed, avoid using the AccessSpecifier enum @@ -178,6 +182,12 @@ public: /// allows for graceful error recovery. void setInvalidDecl() { InvalidDecl = 1; } bool isInvalidDecl() const { return (bool) InvalidDecl; } + + /// isImplicit - Indicates whether the declaration was implicitly + /// generated by the implementation. If false, this declaration + /// was written explicitly in the source code. + bool isImplicit() const { return Implicit; } + void setImplicit(bool I = true) { Implicit = I; } IdentifierNamespace getIdentifierNamespace() const { switch (DeclKind) { diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 583f57854a..567751d710 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -463,9 +463,6 @@ public: return getLexicalDeclContext() != getDeclContext(); } - void setAccess(AccessSpecifier AS) { Access = AS; } - AccessSpecifier getAccess() const { return AccessSpecifier(Access); } - /// getParent - Returns the parent of this method declaration, which /// is the class in which this method is defined. const CXXRecordDecl *getParent() const { @@ -617,16 +614,12 @@ class CXXConstructorDecl : public CXXMethodDecl { /// Explicit - Whether this constructor is explicit. bool Explicit : 1; - /// ImplicitlyDeclared - Whether this constructor was implicitly - /// declared. When false, the constructor was declared by the user. - bool ImplicitlyDeclared : 1; - /// ImplicitlyDefined - Whether this constructor was implicitly /// defined by the compiler. When false, the constructor was defined /// by the user. In C++03, this flag will have the same value as - /// ImplicitlyDeclared. In C++0x, however, a constructor that is + /// Implicit. In C++0x, however, a constructor that is /// explicitly defaulted (i.e., defined with " = default") will have - /// @c !ImplicitlyDeclared && ImplicitlyDefined. + /// @c !Implicit && ImplicitlyDefined. bool ImplicitlyDefined : 1; /// FIXME: Add support for base and member initializers. @@ -636,8 +629,9 @@ class CXXConstructorDecl : public CXXMethodDecl { bool isExplicit, bool isInline, bool isImplicitlyDeclared) : CXXMethodDecl(CXXConstructor, RD, L, N, T, false, isInline, /*PrevDecl=*/0), - Explicit(isExplicit), ImplicitlyDeclared(isImplicitlyDeclared), - ImplicitlyDefined(false) { } + Explicit(isExplicit), ImplicitlyDefined(false) { + setImplicit(isImplicitlyDeclared); + } public: static CXXConstructorDecl *Create(ASTContext &C, CXXRecordDecl *RD, @@ -648,11 +642,6 @@ public: /// isExplicit - Whether this constructor was marked "explicit" or not. bool isExplicit() const { return Explicit; } - /// isImplicitlyDeclared - Whether this constructor was implicitly - /// declared. If false, then this constructor was explicitly - /// declared by the user. - bool isImplicitlyDeclared() const { return ImplicitlyDeclared; } - /// isImplicitlyDefined - Whether this constructor was implicitly /// defined. If false, then this constructor was defined by the /// user. This operation can only be invoked if the constructor has @@ -728,16 +717,12 @@ public: /// }; /// @endcode class CXXDestructorDecl : public CXXMethodDecl { - /// ImplicitlyDeclared - Whether this destructor was implicitly - /// declared. When false, the destructor was declared by the user. - bool ImplicitlyDeclared : 1; - /// ImplicitlyDefined - Whether this destructor was implicitly /// defined by the compiler. When false, the destructor was defined /// by the user. In C++03, this flag will have the same value as - /// ImplicitlyDeclared. In C++0x, however, a destructor that is + /// Implicit. In C++0x, however, a destructor that is /// explicitly defaulted (i.e., defined with " = default") will have - /// @c !ImplicitlyDeclared && ImplicitlyDefined. + /// @c !Implicit && ImplicitlyDefined. bool ImplicitlyDefined : 1; CXXDestructorDecl(CXXRecordDecl *RD, SourceLocation L, @@ -745,8 +730,9 @@ class CXXDestructorDecl : public CXXMethodDecl { bool isInline, bool isImplicitlyDeclared) : CXXMethodDecl(CXXDestructor, RD, L, N, T, false, isInline, /*PrevDecl=*/0), - ImplicitlyDeclared(isImplicitlyDeclared), - ImplicitlyDefined(false) { } + ImplicitlyDefined(false) { + setImplicit(isImplicitlyDeclared); + } public: static CXXDestructorDecl *Create(ASTContext &C, CXXRecordDecl *RD, @@ -754,11 +740,6 @@ public: QualType T, bool isInline, bool isImplicitlyDeclared); - /// isImplicitlyDeclared - Whether this destructor was implicitly - /// declared. If false, then this destructor was explicitly - /// declared by the user. - bool isImplicitlyDeclared() const { return ImplicitlyDeclared; } - /// isImplicitlyDefined - Whether this destructor was implicitly /// defined. If false, then this destructor was defined by the /// user. This operation can only be invoked if the destructor has @@ -857,9 +838,6 @@ public: SourceLocation L,IdentifierInfo *Id, QualType T, ScopedDecl *PrevDecl); - void setAccess(AccessSpecifier AS) { Access = AS; } - AccessSpecifier getAccess() const { return AccessSpecifier(Access); } - // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() == CXXClassVar; } static bool classof(const CXXClassVarDecl *D) { return true; } diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 13a3a93d62..a29f43f1dd 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -1476,7 +1476,7 @@ DIAG(err_base_init_direct_and_virtual, ERROR, "base class initializer %0 names both a direct base class and an" " inherited virtual base class") -// C++ anonymous unions and GNU anonymous structs +// C++ anonymous unions and GNU anonymous structs/unions DIAG(ext_anonymous_union, EXTENSION, "anonymous unions are a GNU extension in C") DIAG(ext_anonymous_struct, EXTENSION, @@ -1491,6 +1491,16 @@ DIAG(err_anonymous_union_member_redecl, ERROR, "member of anonymous union redeclares %0") DIAG(err_anonymous_struct_member_redecl, ERROR, "member of anonymous struct redeclares %0") +DIAG(err_anonymous_record_with_type, ERROR, + "types cannot be declared in an anonymous %select{struct|union}0") +DIAG(err_anonymous_record_with_function, ERROR, + "functions cannot be declared in an anonymous %select{struct|union}0") +DIAG(err_anonymous_record_with_static, ERROR, + "static members cannot be declared in an anonymous %select{struct|union}0") +DIAG(err_anonymous_record_bad_member, ERROR, + "anonymous %select{struct|union}0 can only contain non-static data members") +DIAG(err_anonymous_record_nonpublic_member, ERROR, + "anonymous %select{struct|union}0 cannot contain a %select{private|protected}1 data member") // Derived classes. DIAG(err_dup_virtual, ERROR, diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index bfe8a184e8..b3f19a25b8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -97,6 +97,15 @@ FieldDecl *FieldDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation L, return new (Mem) FieldDecl(Decl::Field, DC, L, Id, T, BW, Mutable, PrevDecl); } +bool FieldDecl::isAnonymousStructOrUnion() const { + if (!isImplicit() || getDeclName()) + return false; + + if (const RecordType *Record = getType()->getAsRecordType()) + return Record->getDecl()->isAnonymousStructOrUnion(); + + return false; +} EnumConstantDecl *EnumConstantDecl::Create(ASTContext &C, EnumDecl *CD, SourceLocation L, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 545f18ed05..7f787de75f 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -153,7 +153,7 @@ bool CXXRecordDecl::hasConstCopyAssignment(ASTContext &Context) const { void CXXRecordDecl::addedConstructor(ASTContext &Context, CXXConstructorDecl *ConDecl) { - if (!ConDecl->isImplicitlyDeclared()) { + if (!ConDecl->isImplicit()) { // Note that we have a user-declared constructor. UserDeclaredConstructor = true; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e1c9efc326..49719ab4f5 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -924,6 +924,48 @@ Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, DS.SetStorageClassSpec(DeclSpec::SCS_unspecified, SourceLocation(), PrevSpec); } + + // C++ [class.union]p2: + // The member-specification of an anonymous union shall only + // define non-static data members. [Note: nested types and + // functions cannot be declared within an anonymous union. ] + for (DeclContext::decl_iterator Mem = Record->decls_begin(), + MemEnd = Record->decls_end(); + Mem != MemEnd; ++Mem) { + if (FieldDecl *FD = dyn_cast<FieldDecl>(*Mem)) { + // C++ [class.union]p3: + // An anonymous union shall not have private or protected + // members (clause 11). + if (FD->getAccess() == AS_protected || FD->getAccess() == AS_private) { + Diag(FD->getLocation(), diag::err_anonymous_record_nonpublic_member) + << (int)Record->isUnion() << (int)(FD->getAccess() == AS_protected); + Invalid = true; + } + } else if ((*Mem)->isImplicit()) { + // Any implicit members are fine. + } else if (RecordDecl *MemRecord = dyn_cast<RecordDecl>(*Mem)) { + if (!MemRecord->isAnonymousStructOrUnion() && + MemRecord->getDeclName()) { + // This is a nested type declaration. + Diag(MemRecord->getLocation(), diag::err_anonymous_record_with_type) + << (int)Record->isUnion(); + Invalid = true; + } + } else { + // We have something that isn't a non-static data + // member. Complain about it. + unsigned DK = diag::err_anonymous_record_bad_member; + if (isa<TypeDecl>(*Mem)) + DK = diag::err_anonymous_record_with_type; + else if (isa<FunctionDecl>(*Mem)) + DK = diag::err_anonymous_record_with_function; + else if (isa<VarDecl>(*Mem)) + DK = diag::err_anonymous_record_with_static; + Diag((*Mem)->getLocation(), DK) + << (int)Record->isUnion(); + Invalid = true; + } + } } else { // FIXME: Check GNU C semantics } @@ -941,6 +983,9 @@ Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, Context.getTypeDeclType(Record), /*BitWidth=*/0, /*Mutable=*/false, /*PrevDecl=*/0); + Anon->setAccess(AS_public); + if (getLangOptions().CPlusPlus) + FieldCollector->Add(cast<FieldDecl>(Anon)); } else { VarDecl::StorageClass SC; switch (DS.getStorageClassSpec()) { @@ -966,6 +1011,7 @@ Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, SC, /*FIXME:LastDeclarator=*/0, DS.getSourceRange().getBegin()); } + Anon->setImplicit(); // Add the anonymous struct/union object to the current // context. We'll be referencing this object when we refer to one of @@ -3176,18 +3222,52 @@ void Sema::ActOnFields(Scope* S, // Verify that all the fields are okay. unsigned NumNamedMembers = 0; llvm::SmallVector<FieldDecl*, 32> RecFields; - llvm::SmallSet<const IdentifierInfo*, 32> FieldIDs; + + // FIXME: Eventually, we'd like to eliminate this in favor of + // checking for redeclarations on-the-fly. + llvm::DenseMap<const IdentifierInfo*, FieldDecl *> FieldIDs; for (unsigned i = 0; i != NumFields; ++i) { - FieldDecl *FD = cast_or_null<FieldDecl>(static_cast<Decl*>(Fields[i])); assert(FD && "missing field decl"); - // Remember all fields. - RecFields.push_back(FD); - // Get the type for the field. Type *FDTy = FD->getType().getTypePtr(); + + if (FD->isAnonymousStructOrUnion()) { + // We have found a field that represents an anonymous struct + // or union. Introduce all of the inner fields (recursively) + // into the list of fields we know about, so that we can produce + // an appropriate error message in cases like: + // + // struct X { + // union { + // int x; + // float f; + // }; + // double x; + // }; + llvm::SmallVector<FieldDecl *, 4> AnonStructUnionFields; + AnonStructUnionFields.push_back(FD); + while (!AnonStructUnionFields.empty()) { + FieldDecl *AnonField = AnonStructUnionFields.back(); + AnonStructUnionFields.pop_back(); + + RecordDecl *AnonRecord + = AnonField->getType()->getAsRecordType()->getDecl(); + for (RecordDecl::field_iterator F = AnonRecord->field_begin(), + FEnd = AnonRecord->field_end(); + F != FEnd; ++F) { + if ((*F)->isAnonymousStructOrUnion()) + AnonStructUnionFields.push_back(*F); + else if (const IdentifierInfo *II = (*F)->getIdentifier()) + FieldIDs[II] = *F; + } + } + } else { + // Remember all fields written by the user. + RecFields.push_back(FD); + } // C99 6.7.2.1p2 - A field may not be a function type. if (FDTy->isFunctionType()) { @@ -3262,23 +3342,16 @@ void Sema::ActOnFields(Scope* S, // Keep track of the number of named members. if (IdentifierInfo *II = FD->getIdentifier()) { // Detect duplicate member names. - if (!FieldIDs.insert(II)) { + if (FieldIDs[II]) { Diag(FD->getLocation(), diag::err_duplicate_member) << II; // Find the previous decl. - SourceLocation PrevLoc; - for (unsigned i = 0; ; ++i) { - assert(i != RecFields.size() && "Didn't find previous def!"); - if (RecFields[i]->getIdentifier() == II) { - PrevLoc = RecFields[i]->getLocation(); - break; - } - } - Diag(PrevLoc, diag::note_previous_definition); + Diag(FieldIDs[II]->getLocation(), diag::note_previous_definition); FD->setInvalidDecl(); EnclosingDecl->setInvalidDecl(); continue; } ++NumNamedMembers; + FieldIDs[II] = FD; } } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 19582c0d79..c5e5aa2f3d 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -427,9 +427,12 @@ void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) { // class itself; this is known as the injected-class-name. For // purposes of access checking, the injected-class-name is treated // as if it were a public member name. - PushOnScopeChains(CXXRecordDecl::Create(Context, Dcl->getTagKind(), - CurContext, Dcl->getLocation(), - Dcl->getIdentifier(), Dcl), S); + RecordDecl *InjectedClassName + = CXXRecordDecl::Create(Context, Dcl->getTagKind(), + CurContext, Dcl->getLocation(), + Dcl->getIdentifier(), Dcl); + InjectedClassName->setImplicit(); + PushOnScopeChains(InjectedClassName, S); } } @@ -789,6 +792,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { /*isInline=*/true, /*isImplicitlyDeclared=*/true); DefaultCon->setAccess(AS_public); + DefaultCon->setImplicit(); ClassDecl->addDecl(Context, DefaultCon); // Notify the class that we've added a constructor. @@ -860,6 +864,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { /*isInline=*/true, /*isImplicitlyDeclared=*/true); CopyConstructor->setAccess(AS_public); + CopyConstructor->setImplicit(); // Add the parameter to the constructor. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyConstructor, @@ -936,6 +941,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { false, 0), /*isStatic=*/false, /*isInline=*/true, 0); CopyAssignment->setAccess(AS_public); + CopyAssignment->setImplicit(); // Add the parameter to the operator. ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment, @@ -964,6 +970,7 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { /*isInline=*/true, /*isImplicitlyDeclared=*/true); Destructor->setAccess(AS_public); + Destructor->setImplicit(); ClassDecl->addDecl(Context, Destructor); } } diff --git a/test/SemaCXX/anonymous-union.cpp b/test/SemaCXX/anonymous-union.cpp index d078d2ff08..a66745b378 100644 --- a/test/SemaCXX/anonymous-union.cpp +++ b/test/SemaCXX/anonymous-union.cpp @@ -66,11 +66,11 @@ struct Redecl { union { int x; // expected-error{{member of anonymous union redeclares 'x'}} float y; - double z; // FIXME: note here + double z; // expected-note{{previous definition is here}} double zz; // expected-note{{previous definition is here}} }; - int z; // FIXME: should complain here! + int z; // expected-error{{duplicate member 'z'}} void zz(); // expected-error{{redefinition of 'zz' as different kind of symbol}} }; @@ -92,8 +92,19 @@ void f() { void g() { union { int i; - float f; + float f2; }; i = 0; - f = 0.0; + f2 = 0.0; } + +struct BadMembers { + union { + struct X { }; // expected-error {{types cannot be declared in an anonymous union}} + struct { int x; int y; } y; + + void f(); // expected-error{{functions cannot be declared in an anonymous union}} + private: int x1; // expected-error{{anonymous union cannot contain a private data member}} + protected: float x2; // expected-error{{anonymous union cannot contain a protected data member}} + }; +}; diff --git a/www/cxx_status.html b/www/cxx_status.html index a4a6741b34..96cd287af8 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -1077,10 +1077,10 @@ welcome!</p> <tr>
<td> 9.5 [class.union]</td>
<td class="complete" align="center">✓</td>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
+ <td class="complete" align="center">✓</td>
+ <td class="medium"></td>
+ <td class="medium"></td>
+ <td>Semantic analysis does not yet check all of the requirements placed on the members of unions.</td>
</tr>
<tr>
<td> 9.6 [class.bit]</td>
|