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.

llvm-svn: 61878
This commit is contained in:
Douglas Gregor 2009-01-07 19:46:03 +00:00
parent 289f59f233
commit f4d332797b
10 changed files with 170 additions and 69 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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; }

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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}}
};
};

View File

@ -1077,10 +1077,10 @@ welcome!</p>
<tr>
<td>&nbsp;&nbsp;9.5 [class.union]</td>
<td class="complete" align="center">&#x2713;</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="complete" align="center">&#x2713;</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>&nbsp;&nbsp;9.6 [class.bit]</td>