forked from OSchip/llvm-project
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. llvm-svn: 61840
This commit is contained in:
parent
1d92d2c813
commit
9ac7a0707d
|
@ -1054,6 +1054,9 @@ class RecordDecl : public TagDecl, public DeclContext {
|
|||
/// If so, this cannot be contained in arrays or other structs as a member.
|
||||
bool HasFlexibleArrayMember : 1;
|
||||
|
||||
///
|
||||
bool AnonymousStructOrUnion : 1;
|
||||
|
||||
protected:
|
||||
RecordDecl(Kind DK, TagKind TK, DeclContext *DC,
|
||||
SourceLocation L, IdentifierInfo *Id);
|
||||
|
@ -1069,6 +1072,23 @@ public:
|
|||
bool hasFlexibleArrayMember() const { return HasFlexibleArrayMember; }
|
||||
void setHasFlexibleArrayMember(bool V) { HasFlexibleArrayMember = V; }
|
||||
|
||||
/// isAnonymousStructOrUnion - Whether this is an anonymous struct
|
||||
/// or union. To be an anonymous struct or union, it must have been
|
||||
/// declared without a name and there must be no objects of this
|
||||
/// type declared, e.g.,
|
||||
/// @code
|
||||
/// union { int i; float f; };
|
||||
/// @endcode
|
||||
/// is an anonymous union but neither of the following are:
|
||||
/// @code
|
||||
/// union X { int i; float f; };
|
||||
/// union { int i; float f; } obj;
|
||||
/// @endcode
|
||||
bool isAnonymousStructOrUnion() const { return AnonymousStructOrUnion; }
|
||||
void setAnonymousStructOrUnion(bool Anon) {
|
||||
AnonymousStructOrUnion = Anon;
|
||||
}
|
||||
|
||||
/// getDefinition - Returns the RecordDecl that actually defines this
|
||||
/// struct/union/class. When determining whether or not a struct/union/class
|
||||
/// is completely defined, one should use this method as opposed to
|
||||
|
|
|
@ -384,8 +384,8 @@ public:
|
|||
return DeclKind == Decl::TranslationUnit || DeclKind == Decl::Namespace;
|
||||
}
|
||||
|
||||
bool isCXXRecord() const {
|
||||
return DeclKind == Decl::CXXRecord;
|
||||
bool isRecord() const {
|
||||
return DeclKind == Decl::Record || DeclKind == Decl::CXXRecord;
|
||||
}
|
||||
|
||||
bool isNamespace() const {
|
||||
|
|
|
@ -1476,6 +1476,22 @@ 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
|
||||
DIAG(ext_anonymous_union, EXTENSION,
|
||||
"anonymous unions are a GNU extension in C")
|
||||
DIAG(ext_anonymous_struct, EXTENSION,
|
||||
"anonymous structs are a GNU extension")
|
||||
DIAG(err_anonymous_union_not_static, ERROR,
|
||||
"anonymous unions at namespace or global scope must be declared 'static'")
|
||||
DIAG(err_anonymous_union_with_storage_spec, ERROR,
|
||||
"anonymous union at class scope must not have a storage specifier")
|
||||
DIAG(err_anonymous_struct_not_member, ERROR,
|
||||
"anonymous structs and classes must be class members")
|
||||
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")
|
||||
|
||||
// Derived classes.
|
||||
DIAG(err_dup_virtual, ERROR,
|
||||
"duplicate 'virtual' in base specifier")
|
||||
|
|
|
@ -290,7 +290,7 @@ OverloadedOperatorKind FunctionDecl::getOverloadedOperator() const {
|
|||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// TagdDecl Implementation
|
||||
// TagDecl Implementation
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
TagDecl* TagDecl::getDefinition(ASTContext& C) const {
|
||||
|
@ -308,6 +308,7 @@ RecordDecl::RecordDecl(Kind DK, TagKind TK, DeclContext *DC, SourceLocation L,
|
|||
: TagDecl(DK, TK, DC, L, Id, 0), DeclContext(DK) {
|
||||
|
||||
HasFlexibleArrayMember = false;
|
||||
AnonymousStructOrUnion = false;
|
||||
assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
|
||||
}
|
||||
|
||||
|
|
|
@ -412,7 +412,7 @@ bool DeclContext::isTransparentContext() const {
|
|||
else if (DeclKind == Decl::LinkageSpec)
|
||||
return true;
|
||||
else if (DeclKind == Decl::Record || DeclKind == Decl::CXXRecord)
|
||||
return false; // FIXME: need to know about anonymous unions/structs
|
||||
return cast<RecordDecl>(this)->isAnonymousStructOrUnion();
|
||||
else if (DeclKind == Decl::Namespace)
|
||||
return false; // FIXME: Check for C++0x inline namespaces
|
||||
|
||||
|
|
|
@ -618,6 +618,7 @@ void RecordDecl::EmitImpl(Serializer& S) const {
|
|||
ScopedDecl::EmitInRec(S);
|
||||
S.EmitBool(isDefinition());
|
||||
S.EmitBool(hasFlexibleArrayMember());
|
||||
S.EmitBool(isAnonymousStructOrUnion());
|
||||
ScopedDecl::EmitOutRec(S);
|
||||
}
|
||||
|
||||
|
@ -630,6 +631,7 @@ RecordDecl* RecordDecl::CreateImpl(Deserializer& D, ASTContext& C) {
|
|||
decl->ScopedDecl::ReadInRec(D, C);
|
||||
decl->setDefinition(D.ReadBool());
|
||||
decl->setHasFlexibleArrayMember(D.ReadBool());
|
||||
decl->setAnonymousStructOrUnion(D.ReadBool());
|
||||
decl->ScopedDecl::ReadOutRec(D, C);
|
||||
|
||||
return decl;
|
||||
|
|
|
@ -299,6 +299,11 @@ public:
|
|||
/// no declarator (e.g. "struct foo;") is parsed.
|
||||
virtual DeclTy *ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS);
|
||||
|
||||
bool InjectAnonymousStructOrUnionMembers(Scope *S, DeclContext *Owner,
|
||||
RecordDecl *AnonRecord);
|
||||
virtual DeclTy *BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS,
|
||||
RecordDecl *Record);
|
||||
|
||||
virtual DeclTy *ActOnTag(Scope *S, unsigned TagType, TagKind TK,
|
||||
SourceLocation KWLoc, const CXXScopeSpec &SS,
|
||||
IdentifierInfo *Name, SourceLocation NameLoc,
|
||||
|
@ -678,6 +683,11 @@ public:
|
|||
DeclRefExpr *BuildDeclRefExpr(NamedDecl *D, QualType Ty, SourceLocation Loc,
|
||||
bool TypeDependent, bool ValueDependent,
|
||||
const CXXScopeSpec *SS = 0);
|
||||
ExprResult
|
||||
BuildAnonymousStructUnionMemberReference(SourceLocation Loc,
|
||||
FieldDecl *Field,
|
||||
Expr *BaseObjectExpr = 0,
|
||||
SourceLocation OpLoc = SourceLocation());
|
||||
ExprResult ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
|
||||
DeclarationName Name,
|
||||
bool HasTrailingLParen,
|
||||
|
|
|
@ -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) {
|
||||
|
@ -808,7 +808,186 @@ Sema::DeclTy *Sema::ParsedFreeStandingDeclSpec(Scope *S, DeclSpec &DS) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return dyn_cast_or_null<TagDecl>(static_cast<Decl *>(DS.getTypeRep()));
|
||||
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 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() ==
|
||||
|
|
|
@ -371,6 +371,143 @@ DeclRefExpr *Sema::BuildDeclRefExpr(NamedDecl *D, QualType Ty, SourceLocation Lo
|
|||
return new DeclRefExpr(D, Ty, Loc, TypeDependent, ValueDependent);
|
||||
}
|
||||
|
||||
/// getObjectForAnonymousRecordDecl - Retrieve the (unnamed) field or
|
||||
/// variable corresponding to the anonymous union or struct whose type
|
||||
/// is Record.
|
||||
static ScopedDecl *getObjectForAnonymousRecordDecl(RecordDecl *Record) {
|
||||
assert(Record->isAnonymousStructOrUnion() &&
|
||||
"Record must be an anonymous struct or union!");
|
||||
|
||||
// FIXME: Once ScopedDecls are directly linked together, this will
|
||||
// be an O(1) operation rather than a slow walk through DeclContext's
|
||||
// vector (which itself will be eliminated). DeclGroups might make
|
||||
// this even better.
|
||||
DeclContext *Ctx = Record->getDeclContext();
|
||||
for (DeclContext::decl_iterator D = Ctx->decls_begin(),
|
||||
DEnd = Ctx->decls_end();
|
||||
D != DEnd; ++D) {
|
||||
if (*D == Record) {
|
||||
// The object for the anonymous struct/union directly
|
||||
// follows its type in the list of declarations.
|
||||
++D;
|
||||
assert(D != DEnd && "Missing object for anonymous record");
|
||||
assert(!cast<ScopedDecl>(*D)->getDeclName() && "Decl should be unnamed");
|
||||
return *D;
|
||||
}
|
||||
}
|
||||
|
||||
assert(false && "Missing object for anonymous record");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Sema::ExprResult
|
||||
Sema::BuildAnonymousStructUnionMemberReference(SourceLocation Loc,
|
||||
FieldDecl *Field,
|
||||
Expr *BaseObjectExpr,
|
||||
SourceLocation OpLoc) {
|
||||
assert(Field->getDeclContext()->isRecord() &&
|
||||
cast<RecordDecl>(Field->getDeclContext())->isAnonymousStructOrUnion()
|
||||
&& "Field must be stored inside an anonymous struct or union");
|
||||
|
||||
// Construct the sequence of field member references
|
||||
// we'll have to perform to get to the field in the anonymous
|
||||
// union/struct. The list of members is built from the field
|
||||
// outward, so traverse it backwards to go from an object in
|
||||
// the current context to the field we found.
|
||||
llvm::SmallVector<FieldDecl *, 4> AnonFields;
|
||||
AnonFields.push_back(Field);
|
||||
VarDecl *BaseObject = 0;
|
||||
DeclContext *Ctx = Field->getDeclContext();
|
||||
do {
|
||||
RecordDecl *Record = cast<RecordDecl>(Ctx);
|
||||
ScopedDecl *AnonObject = getObjectForAnonymousRecordDecl(Record);
|
||||
if (FieldDecl *AnonField = dyn_cast<FieldDecl>(AnonObject))
|
||||
AnonFields.push_back(AnonField);
|
||||
else {
|
||||
BaseObject = cast<VarDecl>(AnonObject);
|
||||
break;
|
||||
}
|
||||
Ctx = Ctx->getParent();
|
||||
} while (Ctx->isRecord() &&
|
||||
cast<RecordDecl>(Ctx)->isAnonymousStructOrUnion());
|
||||
|
||||
// Build the expression that refers to the base object, from
|
||||
// which we will build a sequence of member references to each
|
||||
// of the anonymous union objects and, eventually, the field we
|
||||
// found via name lookup.
|
||||
bool BaseObjectIsPointer = false;
|
||||
unsigned ExtraQuals = 0;
|
||||
if (BaseObject) {
|
||||
// BaseObject is an anonymous struct/union variable (and is,
|
||||
// therefore, not part of another non-anonymous record).
|
||||
delete BaseObjectExpr;
|
||||
|
||||
BaseObjectExpr = new DeclRefExpr(BaseObject, BaseObject->getType(),
|
||||
SourceLocation());
|
||||
ExtraQuals
|
||||
= Context.getCanonicalType(BaseObject->getType()).getCVRQualifiers();
|
||||
} else if (BaseObjectExpr) {
|
||||
// The caller provided the base object expression. Determine
|
||||
// whether its a pointer and whether it adds any qualifiers to the
|
||||
// anonymous struct/union fields we're looking into.
|
||||
QualType ObjectType = BaseObjectExpr->getType();
|
||||
if (const PointerType *ObjectPtr = ObjectType->getAsPointerType()) {
|
||||
BaseObjectIsPointer = true;
|
||||
ObjectType = ObjectPtr->getPointeeType();
|
||||
}
|
||||
ExtraQuals = Context.getCanonicalType(ObjectType).getCVRQualifiers();
|
||||
} else {
|
||||
// We've found a member of an anonymous struct/union that is
|
||||
// inside a non-anonymous struct/union, so in a well-formed
|
||||
// program our base object expression is "this".
|
||||
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext)) {
|
||||
if (!MD->isStatic()) {
|
||||
QualType AnonFieldType
|
||||
= Context.getTagDeclType(
|
||||
cast<RecordDecl>(AnonFields.back()->getDeclContext()));
|
||||
QualType ThisType = Context.getTagDeclType(MD->getParent());
|
||||
if ((Context.getCanonicalType(AnonFieldType)
|
||||
== Context.getCanonicalType(ThisType)) ||
|
||||
IsDerivedFrom(ThisType, AnonFieldType)) {
|
||||
// Our base object expression is "this".
|
||||
BaseObjectExpr = new CXXThisExpr(SourceLocation(),
|
||||
MD->getThisType(Context));
|
||||
BaseObjectIsPointer = true;
|
||||
}
|
||||
} else {
|
||||
return Diag(Loc, diag::err_invalid_member_use_in_static_method)
|
||||
<< Field->getDeclName();
|
||||
}
|
||||
ExtraQuals = MD->getTypeQualifiers();
|
||||
}
|
||||
|
||||
if (!BaseObjectExpr)
|
||||
return Diag(Loc, diag::err_invalid_non_static_member_use)
|
||||
<< Field->getDeclName();
|
||||
}
|
||||
|
||||
// Build the implicit member references to the field of the
|
||||
// anonymous struct/union.
|
||||
Expr *Result = BaseObjectExpr;
|
||||
for (llvm::SmallVector<FieldDecl *, 4>::reverse_iterator
|
||||
FI = AnonFields.rbegin(), FIEnd = AnonFields.rend();
|
||||
FI != FIEnd; ++FI) {
|
||||
QualType MemberType = (*FI)->getType();
|
||||
if (!(*FI)->isMutable()) {
|
||||
unsigned combinedQualifiers
|
||||
= MemberType.getCVRQualifiers() | ExtraQuals;
|
||||
MemberType = MemberType.getQualifiedType(combinedQualifiers);
|
||||
}
|
||||
Result = new MemberExpr(Result, BaseObjectIsPointer, *FI,
|
||||
OpLoc, MemberType);
|
||||
BaseObjectIsPointer = false;
|
||||
ExtraQuals = Context.getCanonicalType(MemberType).getCVRQualifiers();
|
||||
OpLoc = SourceLocation();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// ActOnDeclarationNameExpr - The parser has read some kind of name
|
||||
/// (e.g., a C++ id-expression (C++ [expr.prim]p1)). This routine
|
||||
/// performs lookup on that name and returns an expression that refers
|
||||
|
@ -468,6 +605,12 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
|
|||
}
|
||||
}
|
||||
|
||||
// We may have found a field within an anonymous union or struct
|
||||
// (C++ [class.union]).
|
||||
if (FieldDecl *FD = dyn_cast<FieldDecl>(D))
|
||||
if (cast<RecordDecl>(FD->getDeclContext())->isAnonymousStructOrUnion())
|
||||
return BuildAnonymousStructUnionMemberReference(Loc, FD);
|
||||
|
||||
if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CurContext)) {
|
||||
if (!MD->isStatic()) {
|
||||
// C++ [class.mfct.nonstatic]p2:
|
||||
|
@ -509,7 +652,7 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
|
|||
}
|
||||
}
|
||||
|
||||
if (Ctx && Ctx->isCXXRecord()) {
|
||||
if (Ctx && Ctx->isRecord()) {
|
||||
QualType CtxType = Context.getTagDeclType(cast<CXXRecordDecl>(Ctx));
|
||||
QualType ThisType = Context.getTagDeclType(MD->getParent());
|
||||
if ((Context.getCanonicalType(CtxType)
|
||||
|
@ -623,7 +766,7 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc,
|
|||
for (DeclContext *DC = static_cast<DeclContext*>(SS->getScopeRep());
|
||||
DC; DC = DC->getParent()) {
|
||||
// FIXME: could stop early at namespace scope.
|
||||
if (DC->isCXXRecord()) {
|
||||
if (DC->isRecord()) {
|
||||
CXXRecordDecl *Record = cast<CXXRecordDecl>(DC);
|
||||
if (Context.getTypeDeclType(Record)->isDependentType()) {
|
||||
TypeDependent = true;
|
||||
|
@ -1308,6 +1451,12 @@ ActOnMemberReferenceExpr(Scope *S, ExprTy *Base, SourceLocation OpLoc,
|
|||
<< &Member << BaseExpr->getSourceRange();
|
||||
|
||||
if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl)) {
|
||||
// We may have found a field within an anonymous union or struct
|
||||
// (C++ [class.union]).
|
||||
if (cast<RecordDecl>(FD->getDeclContext())->isAnonymousStructOrUnion())
|
||||
return BuildAnonymousStructUnionMemberReference(MemberLoc, FD,
|
||||
BaseExpr, OpLoc);
|
||||
|
||||
// Figure out the type of the member; see C99 6.5.2.3p3, C++ [expr.ref]
|
||||
// FIXME: Handle address space modifiers
|
||||
QualType MemberType = FD->getType();
|
||||
|
|
|
@ -2169,7 +2169,7 @@ void Sema::AddOperatorCandidates(OverloadedOperatorKind Op, Scope *S,
|
|||
|
||||
// Ignore member functions.
|
||||
if (ScopedDecl *SD = dyn_cast<ScopedDecl>(*I)) {
|
||||
if (SD->getDeclContext()->isCXXRecord())
|
||||
if (SD->getDeclContext()->isRecord())
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -538,7 +538,7 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) {
|
|||
((D.getContext() != Declarator::MemberContext &&
|
||||
(!D.getCXXScopeSpec().isSet() ||
|
||||
!static_cast<DeclContext*>(D.getCXXScopeSpec().getScopeRep())
|
||||
->isCXXRecord())) ||
|
||||
->isRecord())) ||
|
||||
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) {
|
||||
if (D.isFunctionDeclarator())
|
||||
Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type);
|
||||
|
|
Loading…
Reference in New Issue