From bcbffc46f1ad3796c4582fa1e3a9113b5aa26061 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 7 Jan 2009 00:43:41 +0000 Subject: [PATCH] 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 --- include/clang/AST/Decl.h | 24 ++- include/clang/AST/DeclBase.h | 4 +- include/clang/Basic/DiagnosticKinds.def | 16 ++ lib/AST/Decl.cpp | 3 +- lib/AST/DeclBase.cpp | 2 +- lib/AST/DeclSerialization.cpp | 2 + lib/Sema/Sema.h | 10 ++ lib/Sema/SemaDecl.cpp | 202 ++++++++++++++++++++++-- lib/Sema/SemaExpr.cpp | 155 +++++++++++++++++- lib/Sema/SemaOverload.cpp | 2 +- lib/Sema/SemaType.cpp | 2 +- 11 files changed, 402 insertions(+), 20 deletions(-) diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 0401c50454..f5f57b46d0 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1053,7 +1053,10 @@ class RecordDecl : public TagDecl, public DeclContext { /// array member (e.g. int X[]) or if this union contains a struct that does. /// 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); @@ -1068,7 +1071,24 @@ 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 diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 119db2ee21..255067baec 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -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 { diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index d2c82dafc8..13a3a93d62 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -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") diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index bfe0272038..bfe8a184e8 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -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(this)) && "Invalid Kind!"); } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index faaa1045de..cf612c2f3c 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -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(this)->isAnonymousStructOrUnion(); else if (DeclKind == Decl::Namespace) return false; // FIXME: Check for C++0x inline namespaces diff --git a/lib/AST/DeclSerialization.cpp b/lib/AST/DeclSerialization.cpp index 0a298139bd..38af462091 100644 --- a/lib/AST/DeclSerialization.cpp +++ b/lib/AST/DeclSerialization.cpp @@ -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; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 3949695e59..cdec1e6a29 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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, 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(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(static_cast(DS.getTypeRep())); + if (RecordDecl *Record = dyn_cast_or_null(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(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(static_cast(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(Owner) || + (isa(Owner) && + cast(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(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(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(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(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() == diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 8b2aca61ea..bf04042ef8 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -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(*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(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 AnonFields; + AnonFields.push_back(Field); + VarDecl *BaseObject = 0; + DeclContext *Ctx = Field->getDeclContext(); + do { + RecordDecl *Record = cast(Ctx); + ScopedDecl *AnonObject = getObjectForAnonymousRecordDecl(Record); + if (FieldDecl *AnonField = dyn_cast(AnonObject)) + AnonFields.push_back(AnonField); + else { + BaseObject = cast(AnonObject); + break; + } + Ctx = Ctx->getParent(); + } while (Ctx->isRecord() && + cast(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(CurContext)) { + if (!MD->isStatic()) { + QualType AnonFieldType + = Context.getTagDeclType( + cast(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::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 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, 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(D)) + if (cast(FD->getDeclContext())->isAnonymousStructOrUnion()) + return BuildAnonymousStructUnionMemberReference(Loc, FD); if (CXXMethodDecl *MD = dyn_cast(CurContext)) { if (!MD->isStatic()) { @@ -508,8 +651,8 @@ Sema::ExprResult Sema::ActOnDeclarationNameExpr(Scope *S, SourceLocation Loc, } } } - - if (Ctx && Ctx->isCXXRecord()) { + + if (Ctx && Ctx->isRecord()) { QualType CtxType = Context.getTagDeclType(cast(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(SS->getScopeRep()); DC; DC = DC->getParent()) { // FIXME: could stop early at namespace scope. - if (DC->isCXXRecord()) { + if (DC->isRecord()) { CXXRecordDecl *Record = cast(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(MemberDecl)) { + // We may have found a field within an anonymous union or struct + // (C++ [class.union]). + if (cast(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(); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index f829efb71b..704334d789 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2169,7 +2169,7 @@ void Sema::AddOperatorCandidates(OverloadedOperatorKind Op, Scope *S, // Ignore member functions. if (ScopedDecl *SD = dyn_cast(*I)) { - if (SD->getDeclContext()->isCXXRecord()) + if (SD->getDeclContext()->isRecord()) continue; } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index a6441c8d70..f780342a60 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -538,7 +538,7 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) { ((D.getContext() != Declarator::MemberContext && (!D.getCXXScopeSpec().isSet() || !static_cast(D.getCXXScopeSpec().getScopeRep()) - ->isCXXRecord())) || + ->isRecord())) || D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) { if (D.isFunctionDeclarator()) Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type); -- 2.40.0