aboutsummaryrefslogtreecommitdiff
path: root/lib/Sema/SemaDecl.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2009-01-07 00:43:41 +0000
committerDouglas Gregor <dgregor@apple.com>2009-01-07 00:43:41 +0000
commitbcbffc46f1ad3796c4582fa1e3a9113b5aa26061 (patch)
treec87298e6e890898fc9a551c49fe7e7a50f384e70 /lib/Sema/SemaDecl.cpp
parent234a4c286e197f7ca9207d60433d40c802484333 (diff)
Initial implementation of anonymous unions (and, as a GNU extension,
structures and classes) in C++. Covers name lookup and the synthesis and member access for the unnamed objects/fields associated with anonymous unions. Some C++ semantic checks are still missing (anonymous unions can't have function members, static data members, etc.), and there is no support for anonymous structs or unions in C. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61840 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Sema/SemaDecl.cpp')
-rw-r--r--lib/Sema/SemaDecl.cpp202
1 files changed, 193 insertions, 9 deletions
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 79e443d0b4..ea2ea074aa 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -330,7 +330,7 @@ Decl *Sema::LookupDecl(DeclarationName Name, unsigned NSI, Scope *S,
DeclContext *Ctx = static_cast<DeclContext *>(S->getEntity());
while (Ctx && Ctx->isFunctionOrMethod())
Ctx = Ctx->getParent();
- while (Ctx && (Ctx->isNamespace() || Ctx->isCXXRecord())) {
+ while (Ctx && (Ctx->isNamespace() || Ctx->isRecord())) {
// Look for declarations of this name in this scope.
DeclContext::lookup_const_iterator I, E;
for (llvm::tie(I, E) = Ctx->lookup(Context, Name); I != E; ++I) {
@@ -807,8 +807,187 @@ Sema::DeclTy *Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
<< DS.getSourceRange();
return 0;
}
+
+ TagDecl *Tag
+ = dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
+ if (RecordDecl *Record = dyn_cast_or_null<RecordDecl>(Tag)) {
+ if (!Record->getDeclName() && Record->isDefinition() &&
+ !Record->isInvalidDecl())
+ return BuildAnonymousStructOrUnion(S, DS, Record);
+ }
+
+ return Tag;
+}
+
+/// InjectAnonymousStructOrUnionMembers - Inject the members of the
+/// anonymous struct or union AnonRecord into the owning context Owner
+/// and scope S. This routine will be invoked just after we realize
+/// that an unnamed union or struct is actually an anonymous union or
+/// struct, e.g.,
+///
+/// @code
+/// union {
+/// int i;
+/// float f;
+/// }; // InjectAnonymousStructOrUnionMembers called here to inject i and
+/// // f into the surrounding scope.x
+/// @endcode
+///
+/// This routine is recursive, injecting the names of nested anonymous
+/// structs/unions into the owning context and scope as well.
+bool Sema::InjectAnonymousStructOrUnionMembers(Scope *S, DeclContext *Owner,
+ RecordDecl *AnonRecord) {
+ bool Invalid = false;
+ for (RecordDecl::field_iterator F = AnonRecord->field_begin(),
+ FEnd = AnonRecord->field_end();
+ F != FEnd; ++F) {
+ if ((*F)->getDeclName()) {
+ Decl *PrevDecl = LookupDecl((*F)->getDeclName(), Decl::IDNS_Ordinary,
+ S, Owner, false, false, false);
+ if (PrevDecl && !isa<TagDecl>(PrevDecl)) {
+ // C++ [class.union]p2:
+ // The names of the members of an anonymous union shall be
+ // distinct from the names of any other entity in the
+ // scope in which the anonymous union is declared.
+ unsigned diagKind
+ = AnonRecord->isUnion()? diag::err_anonymous_union_member_redecl
+ : diag::err_anonymous_struct_member_redecl;
+ Diag((*F)->getLocation(), diagKind)
+ << (*F)->getDeclName();
+ Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
+ Invalid = true;
+ } else {
+ // C++ [class.union]p2:
+ // For the purpose of name lookup, after the anonymous union
+ // definition, the members of the anonymous union are
+ // considered to have been defined in the scope in which the
+ // anonymous union is declared.
+ Owner->insert(Context, *F);
+ S->AddDecl(*F);
+ IdResolver.AddDecl(*F);
+ }
+ } else if (const RecordType *InnerRecordType
+ = (*F)->getType()->getAsRecordType()) {
+ RecordDecl *InnerRecord = InnerRecordType->getDecl();
+ if (InnerRecord->isAnonymousStructOrUnion())
+ Invalid = Invalid ||
+ InjectAnonymousStructOrUnionMembers(S, Owner, InnerRecord);
+ }
+ }
- return dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
+ return Invalid;
+}
+
+/// ActOnAnonymousStructOrUnion - Handle the declaration of an
+/// anonymous structure or union. Anonymous unions are a C++ feature
+/// (C++ [class.union]) and a GNU C extension; anonymous structures
+/// are a GNU C and GNU C++ extension.
+Sema::DeclTy *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
+ RecordDecl *Record) {
+ DeclContext *Owner = Record->getDeclContext();
+
+ // Diagnose whether this anonymous struct/union is an extension.
+ if (Record->isUnion() && !getLangOptions().CPlusPlus)
+ Diag(Record->getLocation(), diag::ext_anonymous_union);
+ else if (!Record->isUnion())
+ Diag(Record->getLocation(), diag::ext_anonymous_struct);
+
+ // C and C++ require different kinds of checks for anonymous
+ // structs/unions.
+ bool Invalid = false;
+ if (getLangOptions().CPlusPlus) {
+ const char* PrevSpec = 0;
+ // C++ [class.union]p3:
+ // Anonymous unions declared in a named namespace or in the
+ // global namespace shall be declared static.
+ if (DS.getStorageClassSpec() != DeclSpec::SCS_static &&
+ (isa<TranslationUnitDecl>(Owner) ||
+ (isa<NamespaceDecl>(Owner) &&
+ cast<NamespaceDecl>(Owner)->getDeclName()))) {
+ Diag(Record->getLocation(), diag::err_anonymous_union_not_static);
+ Invalid = true;
+
+ // Recover by adding 'static'.
+ DS.SetStorageClassSpec(DeclSpec::SCS_static, SourceLocation(), PrevSpec);
+ }
+ // C++ [class.union]p3:
+ // A storage class is not allowed in a declaration of an
+ // anonymous union in a class scope.
+ else if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified &&
+ isa<RecordDecl>(Owner)) {
+ Diag(DS.getStorageClassSpecLoc(),
+ diag::err_anonymous_union_with_storage_spec);
+ Invalid = true;
+
+ // Recover by removing the storage specifier.
+ DS.SetStorageClassSpec(DeclSpec::SCS_unspecified, SourceLocation(),
+ PrevSpec);
+ }
+ } else {
+ // FIXME: Check GNU C semantics
+ }
+
+ if (!Record->isUnion() && !Owner->isRecord()) {
+ Diag(Record->getLocation(), diag::err_anonymous_struct_not_member);
+ Invalid = true;
+ }
+
+ // Create a declaration for this anonymous struct/union.
+ ScopedDecl *Anon = 0;
+ if (RecordDecl *OwningClass = dyn_cast<RecordDecl>(Owner)) {
+ Anon = FieldDecl::Create(Context, OwningClass, Record->getLocation(),
+ /*IdentifierInfo=*/0,
+ Context.getTypeDeclType(Record),
+ /*BitWidth=*/0, /*Mutable=*/false,
+ /*PrevDecl=*/0);
+ } else {
+ VarDecl::StorageClass SC;
+ switch (DS.getStorageClassSpec()) {
+ default: assert(0 && "Unknown storage class!");
+ case DeclSpec::SCS_unspecified: SC = VarDecl::None; break;
+ case DeclSpec::SCS_extern: SC = VarDecl::Extern; break;
+ case DeclSpec::SCS_static: SC = VarDecl::Static; break;
+ case DeclSpec::SCS_auto: SC = VarDecl::Auto; break;
+ case DeclSpec::SCS_register: SC = VarDecl::Register; break;
+ case DeclSpec::SCS_private_extern: SC = VarDecl::PrivateExtern; break;
+ case DeclSpec::SCS_mutable:
+ // mutable can only appear on non-static class members, so it's always
+ // an error here
+ Diag(Record->getLocation(), diag::err_mutable_nonmember);
+ Invalid = true;
+ SC = VarDecl::None;
+ break;
+ }
+
+ Anon = VarDecl::Create(Context, Owner, Record->getLocation(),
+ /*IdentifierInfo=*/0,
+ Context.getTypeDeclType(Record),
+ SC, /*FIXME:LastDeclarator=*/0,
+ DS.getSourceRange().getBegin());
+ }
+
+ // Add the anonymous struct/union object to the current
+ // context. We'll be referencing this object when we refer to one of
+ // its members.
+ Owner->addDecl(Context, Anon);
+
+ // Inject the members of the anonymous struct/union into the owning
+ // context and into the identifier resolver chain for name lookup
+ // purposes.
+ Invalid = Invalid || InjectAnonymousStructOrUnionMembers(S, Owner, Record);
+
+ // Mark this as an anonymous struct/union type. Note that we do not
+ // do this until after we have already checked and injected the
+ // members of this anonymous struct/union type, because otherwise
+ // the members could be injected twice: once by DeclContext when it
+ // builds its lookup table, and once by
+ // InjectAnonymousStructOrUnionMembers.
+ Record->setAnonymousStructOrUnion(true);
+
+ if (Invalid)
+ Anon->setInvalidDecl();
+
+ return Anon;
}
bool Sema::CheckSingleInitializer(Expr *&Init, QualType DeclType) {
@@ -1170,7 +1349,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl,
FunctionDecl *NewFD;
if (D.getKind() == Declarator::DK_Constructor) {
// This is a C++ constructor declaration.
- assert(DC->isCXXRecord() &&
+ assert(DC->isRecord() &&
"Constructors can only be declared in a member context");
InvalidDecl = InvalidDecl || CheckConstructorDeclarator(D, R, SC);
@@ -1186,7 +1365,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl,
NewFD->setInvalidDecl();
} else if (D.getKind() == Declarator::DK_Destructor) {
// This is a C++ destructor declaration.
- if (DC->isCXXRecord()) {
+ if (DC->isRecord()) {
InvalidDecl = InvalidDecl || CheckDestructorDeclarator(D, R, SC);
NewFD = CXXDestructorDecl::Create(Context,
@@ -1210,7 +1389,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl,
NewFD->setInvalidDecl();
}
} else if (D.getKind() == Declarator::DK_Conversion) {
- if (!DC->isCXXRecord()) {
+ if (!DC->isRecord()) {
Diag(D.getIdentifierLoc(),
diag::err_conv_function_not_member);
return 0;
@@ -1224,7 +1403,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl,
if (InvalidDecl)
NewFD->setInvalidDecl();
}
- } else if (DC->isCXXRecord()) {
+ } else if (DC->isRecord()) {
// This is a C++ method declaration.
NewFD = CXXMethodDecl::Create(Context, cast<CXXRecordDecl>(DC),
D.getIdentifierLoc(), Name, R,
@@ -1473,7 +1652,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl,
return 0;
}
- if (DC->isCXXRecord()) {
+ if (DC->isRecord()) {
// This is a static data member for a C++ class.
NewVD = CXXClassVarDecl::Create(Context, cast<CXXRecordDecl>(DC),
D.getIdentifierLoc(), II,
@@ -2716,6 +2895,7 @@ CreateNewDecl:
// declaration of the same entity, the two will be linked via
// PrevDecl.
TagDecl *New;
+
if (Kind == TagDecl::TK_enum) {
// FIXME: Tag decls should be chained to any simultaneous vardecls, e.g.:
// enum X { A, B, C } D; D should chain to X.
@@ -2769,6 +2949,12 @@ CreateNewDecl:
// Add it to the decl chain.
PushOnScopeChains(New, S);
+ } else if (getLangOptions().CPlusPlus) {
+ // FIXME: We also want to do this for C, but if this tag is
+ // defined within a structure CurContext will point to the context
+ // enclosing the structure, and we would end up inserting the tag
+ // type into the wrong place.
+ CurContext->addDecl(Context, New);
}
return New;
@@ -2869,8 +3055,6 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD,
// FIXME: Chain fielddecls together.
FieldDecl *NewFD;
- // FIXME: We don't want CurContext for C, do we? No, we'll need some
- // other way to determine the current RecordDecl.
NewFD = FieldDecl::Create(Context, Record,
Loc, II, T, BitWidth,
D.getDeclSpec().getStorageClassSpec() ==