diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 2b4d4dfbd2a9..2946e464a7cf 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1024,9 +1024,15 @@ public: public: /// \brief The kind of offsetof node we have. enum Kind { + /// \brief An index into an array. Array = 0x00, + /// \brief A field. Field = 0x01, - Identifier = 0x02 + /// \brief A field in a dependent type, known only by its name. + Identifier = 0x02, + /// \brief An implicit indirection through a C++ base class, when the + /// field found is in a base class. + Base = 0x03 }; private: @@ -1042,6 +1048,8 @@ public: /// - A FieldDecl*, for references to a known field. /// - An IdentifierInfo*, for references to a field with a given name /// when the class type is dependent. + /// - A CXXBaseSpecifier*, for references that look at a field in a + /// base class. uintptr_t Data; public: @@ -1061,6 +1069,10 @@ public: SourceLocation NameLoc) : Range(DotLoc.isValid()? DotLoc : NameLoc, NameLoc), Data(reinterpret_cast(Name) | Identifier) { } + + /// \brief Create an offsetof node that refers into a C++ base class. + explicit OffsetOfNode(const CXXBaseSpecifier *Base) + : Range(), Data(reinterpret_cast(Base) | OffsetOfNode::Base) {} /// \brief Determine what kind of offsetof node this is. Kind getKind() const { @@ -1077,13 +1089,19 @@ public: /// \brief For a field offsetof node, returns the field. FieldDecl *getField() const { assert(getKind() == Field); - return reinterpret_cast (Data & ~(uintptr_t)Mask); + return reinterpret_cast(Data & ~(uintptr_t)Mask); } /// \brief For a field or identifier offsetof node, returns the name of /// the field. IdentifierInfo *getFieldName() const; + /// \brief For a base class node, returns the base specifier. + CXXBaseSpecifier *getBase() const { + assert(getKind() == Base); + return reinterpret_cast(Data & ~(uintptr_t)Mask); + } + /// \brief Retrieve the source range that covers this offsetof node. /// /// For an array element node, the source range contains the locations of diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7233518d58c4..1c2b76eede68 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1414,17 +1414,41 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *E) { if (*Field == MemberDecl) break; } - if (i < RL.getFieldCount()) - Result += CharUnits::fromQuantity( - RL.getFieldOffset(i) / Info.Ctx.getCharWidth()); - else - return false; + assert(i < RL.getFieldCount() && "offsetof field in wrong type"); + Result += CharUnits::fromQuantity( + RL.getFieldOffset(i) / Info.Ctx.getCharWidth()); CurrentType = MemberDecl->getType().getNonReferenceType(); break; } case OffsetOfExpr::OffsetOfNode::Identifier: llvm_unreachable("dependent __builtin_offsetof"); + return false; + + case OffsetOfExpr::OffsetOfNode::Base: { + CXXBaseSpecifier *BaseSpec = ON.getBase(); + if (BaseSpec->isVirtual()) + return false; + + // Find the layout of the class whose base we are looking into. + const RecordType *RT = CurrentType->getAs(); + if (!RT) + return false; + RecordDecl *RD = RT->getDecl(); + const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); + + // Find the base class itself. + CurrentType = BaseSpec->getType(); + const RecordType *BaseRT = CurrentType->getAs(); + if (!BaseRT) + return false; + + // Add the offset to the base. + Result += CharUnits::fromQuantity( + RL.getBaseClassOffset(cast(BaseRT->getDecl())) + / Info.Ctx.getCharWidth()); + break; + } } } return Success(Result.getQuantity(), E); diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index b50bb0ce7e1b..52f627d449e2 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -718,7 +718,11 @@ void StmtPrinter::VisitOffsetOfExpr(OffsetOfExpr *Node) { PrintedSomething = true; continue; } - + + // Skip implicit base indirections. + if (ON.getKind() == OffsetOfExpr::OffsetOfNode::Base) + continue; + // Field or identifier node. IdentifierInfo *Id = ON.getFieldName(); if (!Id) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 3d528f3ccb45..d45bb2f010c0 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -279,6 +279,10 @@ void StmtProfiler::VisitOffsetOfExpr(OffsetOfExpr *S) { case OffsetOfExpr::OffsetOfNode::Identifier: ID.AddPointer(ON.getFieldName()); break; + + case OffsetOfExpr::OffsetOfNode::Base: + // These nodes are implicit, and therefore don't need profiling. + break; } } diff --git a/clang/lib/Frontend/PCHReaderStmt.cpp b/clang/lib/Frontend/PCHReaderStmt.cpp index 8588c8aa1a27..ef6b77026d7f 100644 --- a/clang/lib/Frontend/PCHReaderStmt.cpp +++ b/clang/lib/Frontend/PCHReaderStmt.cpp @@ -462,6 +462,11 @@ unsigned PCHStmtReader::VisitOffsetOfExpr(OffsetOfExpr *E) { case Node::Identifier: E->setComponent(I, Node(Start, Reader.GetIdentifier(Record[Idx++]), End)); break; + + case Node::Base: + // FIXME: Implement this! + llvm_unreachable("PCH for offsetof(base-specifier) not implemented"); + break; } } diff --git a/clang/lib/Frontend/PCHWriterStmt.cpp b/clang/lib/Frontend/PCHWriterStmt.cpp index 3d158a48decc..a1993d37f2dc 100644 --- a/clang/lib/Frontend/PCHWriterStmt.cpp +++ b/clang/lib/Frontend/PCHWriterStmt.cpp @@ -418,6 +418,11 @@ void PCHStmtWriter::VisitOffsetOfExpr(OffsetOfExpr *E) { case OffsetOfExpr::OffsetOfNode::Identifier: Writer.AddIdentifierRef(ON.getFieldName(), Record); break; + + case OffsetOfExpr::OffsetOfNode::Base: + // FIXME: Implement this! + llvm_unreachable("PCH for offsetof(base-specifier) not implemented"); + break; } } for (unsigned I = 0, N = E->getNumExpressions(); I != N; ++I) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 378584825b93..d8d525e99209 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -16,6 +16,7 @@ #include "Lookup.h" #include "AnalysisBasedWarnings.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" @@ -6736,6 +6737,19 @@ Sema::OwningExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, return ExprError(); } + // If the member was found in a base class, introduce OffsetOfNodes for + // the base class indirections. + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + if (IsDerivedFrom(CurrentType, + Context.getTypeDeclType(MemberDecl->getParent()), + Paths)) { + CXXBasePath &Path = Paths.front(); + for (CXXBasePath::iterator B = Path.begin(), BEnd = Path.end(); + B != BEnd; ++B) + Comps.push_back(OffsetOfNode(B->Base)); + } + if (cast(MemberDecl->getDeclContext())-> isAnonymousStructOrUnion()) { llvm::SmallVector Path; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 4e32897df62f..5143b77f592d 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4223,6 +4223,10 @@ TreeTransform::TransformOffsetOfExpr(OffsetOfExpr *E) { continue; break; + + case Node::Base: + // Will be recomputed during the rebuild. + continue; } Components.push_back(Comp); diff --git a/clang/test/SemaCXX/offsetof.cpp b/clang/test/SemaCXX/offsetof.cpp index 4be97a948f66..47c3f22a0ed3 100644 --- a/clang/test/SemaCXX/offsetof.cpp +++ b/clang/test/SemaCXX/offsetof.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -Winvalid-offsetof +// RUN: %clang_cc1 -triple x86_64-apple-darwin10.0.0 -fsyntax-only -verify %s -Winvalid-offsetof struct NonPOD { virtual void f(); @@ -36,3 +36,20 @@ struct has_bitfields { }; int test3 = __builtin_offsetof(struct has_bitfields, j); // expected-error{{cannot compute offset of bit-field 'j'}} + +// offsetof referring to members of a base class. +struct Base1 { + int x; +}; + +struct Base2 { + int y; +}; + +struct Derived2 : public Base1, public Base2 { + int z; +}; + +int derived1[__builtin_offsetof(Derived2, x) == 0? 1 : -1]; +int derived2[__builtin_offsetof(Derived2, y) == 4? 1 : -1]; +int derived3[__builtin_offsetof(Derived2, z) == 8? 1 : -1];