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
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 8b2aca6..bf04042 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -371,6 +371,143 @@
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
@@ -467,6 +604,12 @@
return Diag(Loc, diag::err_undeclared_var_use) << Name;
}
}
+
+ // 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()) {
@@ -508,8 +651,8 @@
}
}
}
-
- 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 @@
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 @@
<< &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();