From 882211c1da17254908e6b36d6e5d4e425f224347 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 28 Apr 2010 22:16:22 +0000 Subject: [PATCH] Completely reimplement __builtin_offsetof, based on a patch by Roberto Amadini. This change introduces a new expression node type, OffsetOfExpr, that describes __builtin_offsetof. Previously, __builtin_offsetof was implemented using a unary operator whose subexpression involved various synthesized array-subscript and member-reference expressions, which was ugly and made it very hard to instantiate as a template. OffsetOfExpr represents the AST more faithfully, with proper type source information and a more compact representation. OffsetOfExpr also has support for dependent __builtin_offsetof expressions; it can be value-dependent, but will never be type-dependent (like sizeof or alignof). This commit introduces template instantiation for __builtin_offsetof as well. There are two major caveats to this patch: 1) CodeGen cannot handle the case where __builtin_offsetof is not a constant expression, so it produces an error. So, to avoid regressing in C, we retain the old UnaryOperator-based __builtin_offsetof implementation in C while using the shiny new OffsetOfExpr implementation in C++. The old implementation can go away once we have proper CodeGen support for this case, which we expect won't cause much trouble in C++. 2) __builtin_offsetof doesn't work well with non-POD class types, particularly when the designated field is found within a base class. I will address this in a subsequent patch. Fixes PR5880 and a bunch of assertions when building Boost.Python tests. llvm-svn: 102542 --- clang/include/clang/AST/Expr.h | 185 +++++++++++++++- clang/include/clang/AST/StmtNodes.def | 1 + .../Checker/PathSensitive/GRExprEngine.h | 4 + clang/include/clang/Frontend/PCHBitCodes.h | 2 + clang/include/clang/Frontend/StmtXML.def | 9 +- clang/lib/AST/Expr.cpp | 67 +++++- clang/lib/AST/ExprConstant.cpp | 66 +++++- clang/lib/AST/StmtPrinter.cpp | 30 +++ clang/lib/AST/StmtProfile.cpp | 24 ++ clang/lib/Checker/GRExprEngine.cpp | 37 +++- clang/lib/CodeGen/CGExprScalar.cpp | 18 +- clang/lib/Frontend/PCHReaderStmt.cpp | 45 ++++ clang/lib/Frontend/PCHWriterStmt.cpp | 32 +++ clang/lib/Frontend/StmtXML.cpp | 5 + clang/lib/Sema/Sema.h | 5 + clang/lib/Sema/SemaChecking.cpp | 4 + clang/lib/Sema/SemaExpr.cpp | 205 +++++++++++++++--- clang/lib/Sema/TreeTransform.h | 71 ++++++ clang/test/PCH/exprs.c | 4 + clang/test/PCH/exprs.h | 13 ++ clang/test/SemaCXX/offsetof.cpp | 10 + .../test/SemaTemplate/instantiate-expr-5.cpp | 20 +- clang/tools/CIndex/CIndex.cpp | 9 + clang/tools/CIndex/CXCursor.cpp | 3 +- 24 files changed, 815 insertions(+), 54 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index d824a56acf1d..2b4d4dfbd2a9 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -223,7 +223,7 @@ public: } /// isConstantInitializer - Returns true if this expression is a constant /// initializer, which can be emitted at compile-time. - bool isConstantInitializer(ASTContext &Ctx) const; + bool isConstantInitializer(ASTContext &Ctx) const; /// EvalResult is a struct with detailed info about an evaluated expression. struct EvalResult { @@ -914,7 +914,7 @@ public: /// /// __builtin_offsetof(type, a.b[10]) is represented as a unary operator whose /// subexpression is a compound literal with the various MemberExpr and -/// ArraySubscriptExpr's applied to it. +/// ArraySubscriptExpr's applied to it. (This is only used in C) /// class UnaryOperator : public Expr { public: @@ -1003,6 +1003,187 @@ public: virtual child_iterator child_end(); }; +/// OffsetOfExpr - [C99 7.17] - This represents an expression of the form +/// offsetof(record-type, member-designator). For example, given: +/// @code +/// struct S { +/// float f; +/// double d; +/// }; +/// struct T { +/// int i; +/// struct S s[10]; +/// }; +/// @endcode +/// we can represent and evaluate the expression @c offsetof(struct T, s[2].d). + +class OffsetOfExpr : public Expr { +public: + // __builtin_offsetof(type, identifier(.identifier|[expr])*) + class OffsetOfNode { + public: + /// \brief The kind of offsetof node we have. + enum Kind { + Array = 0x00, + Field = 0x01, + Identifier = 0x02 + }; + + private: + enum { MaskBits = 2, Mask = 0x03 }; + + /// \brief The source range that covers this part of the designator. + SourceRange Range; + + /// \brief The data describing the designator, which comes in three + /// different forms, depending on the lower two bits. + /// - An unsigned index into the array of Expr*'s stored after this node + /// in memory, for [constant-expression] designators. + /// - 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. + uintptr_t Data; + + public: + /// \brief Create an offsetof node that refers to an array element. + OffsetOfNode(SourceLocation LBracketLoc, unsigned Index, + SourceLocation RBracketLoc) + : Range(LBracketLoc, RBracketLoc), Data((Index << 2) | Array) { } + + /// \brief Create an offsetof node that refers to a field. + OffsetOfNode(SourceLocation DotLoc, FieldDecl *Field, + SourceLocation NameLoc) + : Range(DotLoc.isValid()? DotLoc : NameLoc, NameLoc), + Data(reinterpret_cast(Field) | OffsetOfNode::Field) { } + + /// \brief Create an offsetof node that refers to an identifier. + OffsetOfNode(SourceLocation DotLoc, IdentifierInfo *Name, + SourceLocation NameLoc) + : Range(DotLoc.isValid()? DotLoc : NameLoc, NameLoc), + Data(reinterpret_cast(Name) | Identifier) { } + + /// \brief Determine what kind of offsetof node this is. + Kind getKind() const { + return static_cast(Data & Mask); + } + + /// \brief For an array element node, returns the index into the array + /// of expressions. + unsigned getArrayExprIndex() const { + assert(getKind() == Array); + return Data >> 2; + } + + /// \brief For a field offsetof node, returns the field. + FieldDecl *getField() const { + assert(getKind() == Field); + 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 Retrieve the source range that covers this offsetof node. + /// + /// For an array element node, the source range contains the locations of + /// the square brackets. For a field or identifier node, the source range + /// contains the location of the period (if there is one) and the + /// identifier. + SourceRange getRange() const { return Range; } + }; + +private: + + SourceLocation OperatorLoc, RParenLoc; + // Base type; + TypeSourceInfo *TSInfo; + // Number of sub-components (i.e. instances of OffsetOfNode). + unsigned NumComps; + // Number of sub-expressions (i.e. array subscript expressions). + unsigned NumExprs; + + OffsetOfExpr(ASTContext &C, QualType type, + SourceLocation OperatorLoc, TypeSourceInfo *tsi, + OffsetOfNode* compsPtr, unsigned numComps, + Expr** exprsPtr, unsigned numExprs, + SourceLocation RParenLoc); + + explicit OffsetOfExpr(unsigned numComps, unsigned numExprs) + : Expr(OffsetOfExprClass, EmptyShell()), + TSInfo(0), NumComps(numComps), NumExprs(numExprs) {} + +public: + + static OffsetOfExpr *Create(ASTContext &C, QualType type, + SourceLocation OperatorLoc, TypeSourceInfo *tsi, + OffsetOfNode* compsPtr, unsigned numComps, + Expr** exprsPtr, unsigned numExprs, + SourceLocation RParenLoc); + + static OffsetOfExpr *CreateEmpty(ASTContext &C, + unsigned NumComps, unsigned NumExprs); + + /// getOperatorLoc - Return the location of the operator. + SourceLocation getOperatorLoc() const { return OperatorLoc; } + void setOperatorLoc(SourceLocation L) { OperatorLoc = L; } + + /// \brief Return the location of the right parentheses. + SourceLocation getRParenLoc() const { return RParenLoc; } + void setRParenLoc(SourceLocation R) { RParenLoc = R; } + + TypeSourceInfo *getTypeSourceInfo() const { + return TSInfo; + } + void setTypeSourceInfo(TypeSourceInfo *tsi) { + TSInfo = tsi; + } + + const OffsetOfNode &getComponent(unsigned Idx) { + assert(Idx < NumComps && "Subscript out of range"); + return reinterpret_cast (this + 1)[Idx]; + } + + void setComponent(unsigned Idx, OffsetOfNode ON) { + assert(Idx < NumComps && "Subscript out of range"); + reinterpret_cast (this + 1)[Idx] = ON; + } + + unsigned getNumComponents() const { + return NumComps; + } + + Expr* getIndexExpr(unsigned Idx) { + assert(Idx < NumExprs && "Subscript out of range"); + return reinterpret_cast( + reinterpret_cast(this+1) + NumComps)[Idx]; + } + + void setIndexExpr(unsigned Idx, Expr* E) { + assert(Idx < NumComps && "Subscript out of range"); + reinterpret_cast( + reinterpret_cast(this+1) + NumComps)[Idx] = E; + } + + unsigned getNumExpressions() const { + return NumExprs; + } + + virtual SourceRange getSourceRange() const { + return SourceRange(OperatorLoc, RParenLoc); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == OffsetOfExprClass; + } + + static bool classof(const OffsetOfExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); +}; + /// SizeOfAlignOfExpr - [C99 6.5.3.4] - This is for sizeof/alignof, both of /// types and expressions. class SizeOfAlignOfExpr : public Expr { diff --git a/clang/include/clang/AST/StmtNodes.def b/clang/include/clang/AST/StmtNodes.def index 4e80ecbad562..3a23e49148d5 100644 --- a/clang/include/clang/AST/StmtNodes.def +++ b/clang/include/clang/AST/StmtNodes.def @@ -78,6 +78,7 @@ EXPR(StringLiteral , Expr) EXPR(CharacterLiteral , Expr) EXPR(ParenExpr , Expr) EXPR(UnaryOperator , Expr) +EXPR(OffsetOfExpr , Expr) EXPR(SizeOfAlignOfExpr , Expr) EXPR(ArraySubscriptExpr , Expr) EXPR(CallExpr , Expr) diff --git a/clang/include/clang/Checker/PathSensitive/GRExprEngine.h b/clang/include/clang/Checker/PathSensitive/GRExprEngine.h index 85c2a6912b08..f04ca75084b7 100644 --- a/clang/include/clang/Checker/PathSensitive/GRExprEngine.h +++ b/clang/include/clang/Checker/PathSensitive/GRExprEngine.h @@ -342,6 +342,10 @@ public: /// VisitReturnStmt - Transfer function logic for return statements. void VisitReturnStmt(ReturnStmt* R, ExplodedNode* Pred, ExplodedNodeSet& Dst); + + /// VisitOffsetOfExpr - Transfer function for offsetof. + void VisitOffsetOfExpr(OffsetOfExpr* Ex, ExplodedNode* Pred, + ExplodedNodeSet& Dst); /// VisitSizeOfAlignOfExpr - Transfer function for sizeof. void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, ExplodedNode* Pred, diff --git a/clang/include/clang/Frontend/PCHBitCodes.h b/clang/include/clang/Frontend/PCHBitCodes.h index 457e63332f7a..1640afb4122a 100644 --- a/clang/include/clang/Frontend/PCHBitCodes.h +++ b/clang/include/clang/Frontend/PCHBitCodes.h @@ -603,6 +603,8 @@ namespace clang { EXPR_PAREN, /// \brief A UnaryOperator record. EXPR_UNARY_OPERATOR, + /// \brief An OffsetOfExpr record. + EXPR_OFFSETOF, /// \brief A SizefAlignOfExpr record. EXPR_SIZEOF_ALIGN_OF, /// \brief An ArraySubscriptExpr record. diff --git a/clang/include/clang/Frontend/StmtXML.def b/clang/include/clang/Frontend/StmtXML.def index 2f0da9e7b117..f63761a908af 100644 --- a/clang/include/clang/Frontend/StmtXML.def +++ b/clang/include/clang/Frontend/StmtXML.def @@ -254,7 +254,7 @@ NODE_XML(UnaryOperator, "UnaryOperator") // op(expr) or (expr)op ENUM_XML(UnaryOperator::Real, "__real") ENUM_XML(UnaryOperator::Imag, "__imag") ENUM_XML(UnaryOperator::Extension, "__extension__") - ENUM_XML(UnaryOperator::OffsetOf, "__builtin_offsetof") + ENUM_XML(UnaryOperator::OffsetOf, "__builtin_offsetof") END_ENUM_XML SUB_NODE_XML(Expr) // expr END_NODE_XML @@ -311,6 +311,13 @@ NODE_XML(ConditionalOperator, "ConditionalOperator") // expr1 ? expr2 : expr3 SUB_NODE_XML(Expr) // expr3 END_NODE_XML +NODE_XML(OffsetOfExpr, "OffsetOfExpr") // offsetof(basetype, components) + ATTRIBUTE_FILE_LOCATION_XML + TYPE_ATTRIBUTE_XML(getTypeSourceInfo()->getType()) + ATTRIBUTE_XML(getNumComponents(), "num_components") + SUB_NODE_SEQUENCE_XML(OffsetOfExpr::OffsetOfNode) +END_NODE_XML + NODE_XML(SizeOfAlignOfExpr, "SizeOfAlignOfExpr") // sizeof(expr) or alignof(expr) ATTRIBUTE_FILE_LOCATION_XML TYPE_ATTRIBUTE_XML(getType()) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 8770bfd3aa4e..308f6b4dc022 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -549,6 +549,57 @@ QualType CallExpr::getCallReturnType() const { return FnType->getResultType(); } +OffsetOfExpr *OffsetOfExpr::Create(ASTContext &C, QualType type, + SourceLocation OperatorLoc, + TypeSourceInfo *tsi, + OffsetOfNode* compsPtr, unsigned numComps, + Expr** exprsPtr, unsigned numExprs, + SourceLocation RParenLoc) { + void *Mem = C.Allocate(sizeof(OffsetOfExpr) + + sizeof(OffsetOfNode) * numComps + + sizeof(Expr*) * numExprs); + + return new (Mem) OffsetOfExpr(C, type, OperatorLoc, tsi, compsPtr, numComps, + exprsPtr, numExprs, RParenLoc); +} + +OffsetOfExpr *OffsetOfExpr::CreateEmpty(ASTContext &C, + unsigned numComps, unsigned numExprs) { + void *Mem = C.Allocate(sizeof(OffsetOfExpr) + + sizeof(OffsetOfNode) * numComps + + sizeof(Expr*) * numExprs); + return new (Mem) OffsetOfExpr(numComps, numExprs); +} + +OffsetOfExpr::OffsetOfExpr(ASTContext &C, QualType type, + SourceLocation OperatorLoc, TypeSourceInfo *tsi, + OffsetOfNode* compsPtr, unsigned numComps, + Expr** exprsPtr, unsigned numExprs, + SourceLocation RParenLoc) + : Expr(OffsetOfExprClass, type, /*TypeDependent=*/false, + /*ValueDependent=*/tsi->getType()->isDependentType() || + hasAnyTypeDependentArguments(exprsPtr, numExprs) || + hasAnyValueDependentArguments(exprsPtr, numExprs)), + OperatorLoc(OperatorLoc), RParenLoc(RParenLoc), TSInfo(tsi), + NumComps(numComps), NumExprs(numExprs) +{ + for(unsigned i = 0; i < numComps; ++i) { + setComponent(i, compsPtr[i]); + } + + for(unsigned i = 0; i < numExprs; ++i) { + setIndexExpr(i, exprsPtr[i]); + } +} + +IdentifierInfo *OffsetOfExpr::OffsetOfNode::getFieldName() const { + assert(getKind() == Field || getKind() == Identifier); + if (getKind() == Field) + return getField()->getIdentifier(); + + return reinterpret_cast (Data & ~(uintptr_t)Mask); +} + MemberExpr *MemberExpr::Create(ASTContext &C, Expr *base, bool isarrow, NestedNameSpecifier *qual, SourceRange qualrange, @@ -1891,7 +1942,7 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case UnaryOperator::AddrOf: case UnaryOperator::Deref: return ICEDiag(2, E->getLocStart()); - + case UnaryOperator::OffsetOf: case UnaryOperator::Extension: case UnaryOperator::LNot: case UnaryOperator::Plus: @@ -1900,7 +1951,9 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case UnaryOperator::Real: case UnaryOperator::Imag: return CheckICE(Exp->getSubExpr(), Ctx); - case UnaryOperator::OffsetOf: + } + } + case Expr::OffsetOfExprClass: { // Note that per C99, offsetof must be an ICE. And AFAIK, using // Evaluate matches the proposed gcc behavior for cases like // "offsetof(struct s{int x[4];}, x[!.0])". This doesn't affect @@ -1908,7 +1961,6 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { // array subscripts that aren't ICEs, and if the array subscripts // are ICEs, the value of the offsetof must be an integer constant. return CheckEvalInICE(E, Ctx); - } } case Expr::SizeOfAlignOfExprClass: { const SizeOfAlignOfExpr *Exp = cast(E); @@ -2702,6 +2754,15 @@ Stmt::child_iterator ParenExpr::child_end() { return &Val+1; } Stmt::child_iterator UnaryOperator::child_begin() { return &Val; } Stmt::child_iterator UnaryOperator::child_end() { return &Val+1; } +// OffsetOfExpr +Stmt::child_iterator OffsetOfExpr::child_begin() { + return reinterpret_cast (reinterpret_cast (this + 1) + + NumComps); +} +Stmt::child_iterator OffsetOfExpr::child_end() { + return child_iterator(&*child_begin() + NumExprs); +} + // SizeOfAlignOfExpr Stmt::child_iterator SizeOfAlignOfExpr::child_begin() { // If this is of a type and the type is a VLA type (and not a typedef), the diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a52cf6fe4c81..7233518d58c4 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -16,7 +16,9 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeLoc.h" #include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/Expr.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/SmallString.h" @@ -228,7 +230,7 @@ public: APValue VisitStmt(Stmt *S) { return APValue(); } - + APValue VisitParenExpr(ParenExpr *E) { return Visit(E->getSubExpr()); } APValue VisitDeclRefExpr(DeclRefExpr *E); APValue VisitPredefinedExpr(PredefinedExpr *E) { return APValue(E); } @@ -828,6 +830,7 @@ public: bool VisitCallExpr(CallExpr *E); bool VisitBinaryOperator(const BinaryOperator *E); + bool VisitOffsetOfExpr(const OffsetOfExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); bool VisitConditionalOperator(const ConditionalOperator *E); @@ -1372,6 +1375,61 @@ bool IntExprEvaluator::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E) { return Success(Info.Ctx.getTypeSizeInChars(SrcTy).getQuantity(), E); } +bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *E) { + CharUnits Result; + unsigned n = E->getNumComponents(); + OffsetOfExpr* OOE = const_cast(E); + if (n == 0) + return false; + QualType CurrentType = E->getTypeSourceInfo()->getType(); + for (unsigned i = 0; i != n; ++i) { + OffsetOfExpr::OffsetOfNode ON = OOE->getComponent(i); + switch (ON.getKind()) { + case OffsetOfExpr::OffsetOfNode::Array: { + Expr *Idx = OOE->getIndexExpr(ON.getArrayExprIndex()); + APSInt IdxResult; + if (!EvaluateInteger(Idx, IdxResult, Info)) + return false; + const ArrayType *AT = Info.Ctx.getAsArrayType(CurrentType); + if (!AT) + return false; + CurrentType = AT->getElementType(); + CharUnits ElementSize = Info.Ctx.getTypeSizeInChars(CurrentType); + Result += IdxResult.getSExtValue() * ElementSize; + break; + } + + case OffsetOfExpr::OffsetOfNode::Field: { + FieldDecl *MemberDecl = ON.getField(); + const RecordType *RT = CurrentType->getAs(); + if (!RT) + return false; + RecordDecl *RD = RT->getDecl(); + const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); + unsigned i = 0; + // FIXME: It would be nice if we didn't have to loop here! + for (RecordDecl::field_iterator Field = RD->field_begin(), + FieldEnd = RD->field_end(); + Field != FieldEnd; (void)++Field, ++i) { + if (*Field == MemberDecl) + break; + } + if (i < RL.getFieldCount()) + Result += CharUnits::fromQuantity( + RL.getFieldOffset(i) / Info.Ctx.getCharWidth()); + else + return false; + CurrentType = MemberDecl->getType().getNonReferenceType(); + break; + } + + case OffsetOfExpr::OffsetOfNode::Identifier: + llvm_unreachable("dependent __builtin_offsetof"); + } + } + return Success(Result.getQuantity(), E); +} + bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { // Special case unary operators that do not need their subexpression // evaluated. offsetof/sizeof/alignof are all special. @@ -1380,12 +1438,12 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { // directly Evaluate it as an l-value. APValue LV; if (!EvaluateLValue(E->getSubExpr(), LV, Info)) - return false; + return false; if (LV.getLValueBase()) - return false; + return false; return Success(LV.getLValueOffset().getQuantity(), E); } - + if (E->getOpcode() == UnaryOperator::LNot) { // LNot's operand isn't necessarily an integer, so we handle it specially. bool bres; diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 3996528287dc..b50bb0ce7e1b 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/PrettyPrinter.h" #include "llvm/Support/Format.h" +#include "clang/AST/Expr.h" using namespace clang; //===----------------------------------------------------------------------===// @@ -703,6 +704,35 @@ void StmtPrinter::VisitUnaryOffsetOf(UnaryOperator *Node) { OS << ")"; } +void StmtPrinter::VisitOffsetOfExpr(OffsetOfExpr *Node) { + OS << "__builtin_offsetof("; + OS << Node->getTypeSourceInfo()->getType().getAsString() << ", "; + bool PrintedSomething = false; + for (unsigned i = 0, n = Node->getNumComponents(); i < n; ++i) { + OffsetOfExpr::OffsetOfNode ON = Node->getComponent(i); + if (ON.getKind() == OffsetOfExpr::OffsetOfNode::Array) { + // Array node + OS << "["; + PrintExpr(Node->getIndexExpr(ON.getArrayExprIndex())); + OS << "]"; + PrintedSomething = true; + continue; + } + + // Field or identifier node. + IdentifierInfo *Id = ON.getFieldName(); + if (!Id) + continue; + + if (PrintedSomething) + OS << "."; + else + PrintedSomething = true; + OS << Id->getName(); + } + OS << ")"; +} + void StmtPrinter::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node) { OS << (Node->isSizeOf() ? "sizeof" : "__alignof"); if (Node->isArgumentType()) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 3a19ec212c14..3d528f3ccb45 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -261,6 +261,30 @@ void StmtProfiler::VisitUnaryOperator(UnaryOperator *S) { ID.AddInteger(S->getOpcode()); } +void StmtProfiler::VisitOffsetOfExpr(OffsetOfExpr *S) { + VisitType(S->getTypeSourceInfo()->getType()); + unsigned n = S->getNumComponents(); + for (unsigned i = 0; i < n; ++i) { + const OffsetOfExpr::OffsetOfNode& ON = S->getComponent(i); + ID.AddInteger(ON.getKind()); + switch (ON.getKind()) { + case OffsetOfExpr::OffsetOfNode::Array: + // Expressions handled below. + break; + + case OffsetOfExpr::OffsetOfNode::Field: + VisitDecl(ON.getField()); + break; + + case OffsetOfExpr::OffsetOfNode::Identifier: + ID.AddPointer(ON.getFieldName()); + break; + } + } + + VisitExpr(S); +} + void StmtProfiler::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *S) { VisitExpr(S); ID.AddBoolean(S->isSizeOf()); diff --git a/clang/lib/Checker/GRExprEngine.cpp b/clang/lib/Checker/GRExprEngine.cpp index c11a16ff7b4c..67090b86694f 100644 --- a/clang/lib/Checker/GRExprEngine.cpp +++ b/clang/lib/Checker/GRExprEngine.cpp @@ -823,6 +823,10 @@ void GRExprEngine::Visit(Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst) { VisitReturnStmt(cast(S), Pred, Dst); break; + case Stmt::OffsetOfExprClass: + VisitOffsetOfExpr(cast(S), Pred, Dst); + break; + case Stmt::SizeOfAlignOfExprClass: VisitSizeOfAlignOfExpr(cast(S), Pred, Dst); break; @@ -2611,6 +2615,21 @@ void GRExprEngine::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr* Ex, ValMgr.makeIntVal(amt.getQuantity(), Ex->getType()))); } +void GRExprEngine::VisitOffsetOfExpr(OffsetOfExpr* OOE, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + Expr::EvalResult Res; + if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { + const APSInt &IV = Res.Val.getInt(); + assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); + assert(OOE->getType()->isIntegerType()); + assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); + SVal X = ValMgr.makeIntVal(IV); + MakeNode(Dst, OOE, Pred, GetState(Pred)->BindExpr(OOE, X)); + return; + } + // FIXME: Handle the case where __builtin_offsetof is not a constant. + Dst.Add(Pred); +} void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, ExplodedNodeSet& Dst, bool asLValue) { @@ -2692,19 +2711,19 @@ void GRExprEngine::VisitUnaryOperator(UnaryOperator* U, ExplodedNode* Pred, case UnaryOperator::OffsetOf: { Expr::EvalResult Res; if (U->Evaluate(Res, getContext()) && Res.Val.isInt()) { - const APSInt &IV = Res.Val.getInt(); - assert(IV.getBitWidth() == getContext().getTypeSize(U->getType())); - assert(U->getType()->isIntegerType()); - assert(IV.isSigned() == U->getType()->isSignedIntegerType()); - SVal X = ValMgr.makeIntVal(IV); - MakeNode(Dst, U, Pred, GetState(Pred)->BindExpr(U, X)); - return; - } + const APSInt &IV = Res.Val.getInt(); + assert(IV.getBitWidth() == getContext().getTypeSize(U->getType())); + assert(U->getType()->isIntegerType()); + assert(IV.isSigned() == U->getType()->isSignedIntegerType()); + SVal X = ValMgr.makeIntVal(IV); + MakeNode(Dst, U, Pred, GetState(Pred)->BindExpr(U, X)); + return; + } // FIXME: Handle the case where __builtin_offsetof is not a constant. Dst.Add(Pred); return; } - + case UnaryOperator::Plus: assert (!asLValue); // FALL-THROUGH. case UnaryOperator::Extension: { diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 842590b24ca8..984968802382 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -133,6 +133,7 @@ public: CGF.getContext().typesAreCompatible( E->getArgType1(), E->getArgType2())); } + Value *VisitOffsetOfExpr(const OffsetOfExpr *E); Value *VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E); Value *VisitAddrLabelExpr(const AddrLabelExpr *E) { llvm::Value *V = CGF.GetAddrOfLabel(E->getLabel()); @@ -242,7 +243,7 @@ public: return Visit(E->getSubExpr()); } Value *VisitUnaryOffsetOf(const UnaryOperator *E); - + // C++ Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) { return Visit(DAE->getExpr()); @@ -1030,6 +1031,21 @@ Value *ScalarExprEmitter::VisitUnaryLNot(const UnaryOperator *E) { return Builder.CreateZExt(BoolVal, ConvertType(E->getType()), "lnot.ext"); } +Value *ScalarExprEmitter::VisitOffsetOfExpr(const OffsetOfExpr *E) { + Expr::EvalResult Result; + if(E->Evaluate(Result, CGF.getContext())) + return llvm::ConstantInt::get(VMContext, Result.Val.getInt()); + + // FIXME: Cannot support code generation for non-constant offsetof. + unsigned DiagID = CGF.CGM.getDiags().getCustomDiagID(Diagnostic::Error, + "cannot compile non-constant __builtin_offsetof"); + CGF.CGM.getDiags().Report(CGF.getContext().getFullLoc(E->getLocStart()), + DiagID) + << E->getSourceRange(); + + return llvm::Constant::getNullValue(ConvertType(E->getType())); +} + /// VisitSizeOfAlignOfExpr - Return the size or alignment of the type of /// argument of the sizeof expression as an integer. Value * diff --git a/clang/lib/Frontend/PCHReaderStmt.cpp b/clang/lib/Frontend/PCHReaderStmt.cpp index 5e08be138a16..8588c8aa1a27 100644 --- a/clang/lib/Frontend/PCHReaderStmt.cpp +++ b/clang/lib/Frontend/PCHReaderStmt.cpp @@ -71,6 +71,7 @@ namespace { unsigned VisitCharacterLiteral(CharacterLiteral *E); unsigned VisitParenExpr(ParenExpr *E); unsigned VisitUnaryOperator(UnaryOperator *E); + unsigned VisitOffsetOfExpr(OffsetOfExpr *E); unsigned VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); unsigned VisitArraySubscriptExpr(ArraySubscriptExpr *E); unsigned VisitCallExpr(CallExpr *E); @@ -432,6 +433,44 @@ unsigned PCHStmtReader::VisitUnaryOperator(UnaryOperator *E) { return 1; } +unsigned PCHStmtReader::VisitOffsetOfExpr(OffsetOfExpr *E) { + typedef OffsetOfExpr::OffsetOfNode Node; + VisitExpr(E); + assert(E->getNumComponents() == Record[Idx]); + ++Idx; + assert(E->getNumExpressions() == Record[Idx]); + ++Idx; + E->setOperatorLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); + E->setRParenLoc(SourceLocation::getFromRawEncoding(Record[Idx++])); + E->setTypeSourceInfo(Reader.GetTypeSourceInfo(Record, Idx)); + for (unsigned I = 0, N = E->getNumComponents(); I != N; ++I) { + Node::Kind Kind = static_cast(Record[Idx++]); + SourceLocation Start = SourceLocation::getFromRawEncoding(Record[Idx++]); + SourceLocation End = SourceLocation::getFromRawEncoding(Record[Idx++]); + switch (Kind) { + case Node::Array: + E->setComponent(I, Node(Start, Record[Idx++], End)); + break; + + case Node::Field: + E->setComponent(I, + Node(Start, + dyn_cast_or_null(Reader.GetDecl(Record[Idx++])), + End)); + break; + + case Node::Identifier: + E->setComponent(I, Node(Start, Reader.GetIdentifier(Record[Idx++]), End)); + break; + } + } + + for (unsigned I = 0, N = E->getNumExpressions(); I != N; ++I) + E->setIndexExpr(I, cast_or_null(StmtStack[StmtStack.size() - N + I])); + + return E->getNumExpressions(); +} + unsigned PCHStmtReader::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { VisitExpr(E); E->setSizeof(Record[Idx++]); @@ -1105,6 +1144,12 @@ Stmt *PCHReader::ReadStmt(llvm::BitstreamCursor &Cursor) { S = new (Context) UnaryOperator(Empty); break; + case pch::EXPR_OFFSETOF: + S = OffsetOfExpr::CreateEmpty(*Context, + Record[PCHStmtReader::NumExprFields], + Record[PCHStmtReader::NumExprFields + 1]); + break; + case pch::EXPR_SIZEOF_ALIGN_OF: S = new (Context) SizeOfAlignOfExpr(Empty); break; diff --git a/clang/lib/Frontend/PCHWriterStmt.cpp b/clang/lib/Frontend/PCHWriterStmt.cpp index a0ea5c9553c1..3d158a48decc 100644 --- a/clang/lib/Frontend/PCHWriterStmt.cpp +++ b/clang/lib/Frontend/PCHWriterStmt.cpp @@ -62,6 +62,7 @@ namespace { void VisitCharacterLiteral(CharacterLiteral *E); void VisitParenExpr(ParenExpr *E); void VisitUnaryOperator(UnaryOperator *E); + void VisitOffsetOfExpr(OffsetOfExpr *E); void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); void VisitArraySubscriptExpr(ArraySubscriptExpr *E); void VisitCallExpr(CallExpr *E); @@ -393,6 +394,37 @@ void PCHStmtWriter::VisitUnaryOperator(UnaryOperator *E) { Code = pch::EXPR_UNARY_OPERATOR; } +void PCHStmtWriter::VisitOffsetOfExpr(OffsetOfExpr *E) { + VisitExpr(E); + Record.push_back(E->getNumComponents()); + Record.push_back(E->getNumExpressions()); + Writer.AddSourceLocation(E->getOperatorLoc(), Record); + Writer.AddSourceLocation(E->getRParenLoc(), Record); + Writer.AddTypeSourceInfo(E->getTypeSourceInfo(), Record); + for (unsigned I = 0, N = E->getNumComponents(); I != N; ++I) { + const OffsetOfExpr::OffsetOfNode &ON = E->getComponent(I); + Record.push_back(ON.getKind()); // FIXME: Stable encoding + Writer.AddSourceLocation(ON.getRange().getBegin(), Record); + Writer.AddSourceLocation(ON.getRange().getEnd(), Record); + switch (ON.getKind()) { + case OffsetOfExpr::OffsetOfNode::Array: + Record.push_back(ON.getArrayExprIndex()); + break; + + case OffsetOfExpr::OffsetOfNode::Field: + Writer.AddDeclRef(ON.getField(), Record); + break; + + case OffsetOfExpr::OffsetOfNode::Identifier: + Writer.AddIdentifierRef(ON.getFieldName(), Record); + break; + } + } + for (unsigned I = 0, N = E->getNumExpressions(); I != N; ++I) + Writer.WriteSubStmt(E->getIndexExpr(I)); + Code = pch::EXPR_OFFSETOF; +} + void PCHStmtWriter::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { VisitExpr(E); Record.push_back(E->isSizeOf()); diff --git a/clang/lib/Frontend/StmtXML.cpp b/clang/lib/Frontend/StmtXML.cpp index ce474d365390..21dc0ba0a188 100644 --- a/clang/lib/Frontend/StmtXML.cpp +++ b/clang/lib/Frontend/StmtXML.cpp @@ -125,6 +125,7 @@ namespace { void VisitFloatingLiteral(FloatingLiteral *Node); void VisitStringLiteral(StringLiteral *Str); void VisitUnaryOperator(UnaryOperator *Node); + void VisitOffsetOfExpr(OffsetOfExpr *Node); void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node); void VisitMemberExpr(MemberExpr *Node); void VisitExtVectorElementExpr(ExtVectorElementExpr *Node); @@ -308,6 +309,10 @@ void StmtXML::VisitUnaryOperator(UnaryOperator *Node) { Doc.addAttribute("op_code", getOpcodeStr(Node->getOpcode())); } +void StmtXML::OffsetOfExpr(OffsetOfExpr *Node) { + DumpExpr(Node); +} + void StmtXML::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node) { DumpExpr(Node); Doc.addAttribute("is_sizeof", Node->isSizeOf() ? "sizeof" : "alignof"); diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index fb65862fe4b1..5508938ff920 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -1986,6 +1986,11 @@ public: SourceLocation RPLoc); // "({..})" /// __builtin_offsetof(type, a.b[123][456].c) + OwningExprResult BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, + TypeSourceInfo *TInfo, + OffsetOfComponent *CompPtr, + unsigned NumComponents, + SourceLocation RParenLoc); virtual OwningExprResult ActOnBuiltinOffsetOf(Scope *S, SourceLocation BuiltinLoc, SourceLocation TypeLoc, diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 0373ca800691..91a3cbe071d1 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1950,6 +1950,10 @@ IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) { return GetExprRange(C, UO->getSubExpr(), MaxWidth); } } + + if (dyn_cast(E)) { + IntRange::forType(C, E->getType()); + } FieldDecl *BitField = E->getBitField(); if (BitField) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index bdd1cfb66b89..6d5749b0e8d3 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18,8 +18,10 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/TypeLoc.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -6450,7 +6452,7 @@ Action::OwningExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, case UnaryOperator::OffsetOf: assert(false && "Invalid unary operator"); break; - + case UnaryOperator::PreInc: case UnaryOperator::PreDec: case UnaryOperator::PostInc: @@ -6608,6 +6610,134 @@ Sema::ActOnStmtExpr(SourceLocation LPLoc, StmtArg substmt, return Owned(new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc)); } +Sema::OwningExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc, + TypeSourceInfo *TInfo, + OffsetOfComponent *CompPtr, + unsigned NumComponents, + SourceLocation RParenLoc) { + QualType ArgTy = TInfo->getType(); + bool Dependent = ArgTy->isDependentType(); + SourceRange TypeRange = TInfo->getTypeLoc().getSourceRange(); + + // We must have at least one component that refers to the type, and the first + // one is known to be a field designator. Verify that the ArgTy represents + // a struct/union/class. + if (!Dependent && !ArgTy->isRecordType()) + return ExprError(Diag(BuiltinLoc, diag::err_offsetof_record_type) + << ArgTy << TypeRange); + + // Type must be complete per C99 7.17p3 because a declaring a variable + // with an incomplete type would be ill-formed. + if (!Dependent + && RequireCompleteType(BuiltinLoc, ArgTy, + PDiag(diag::err_offsetof_incomplete_type) + << TypeRange)) + return ExprError(); + + // offsetof with non-identifier designators (e.g. "offsetof(x, a.b[c])") are a + // GCC extension, diagnose them. + // FIXME: This diagnostic isn't actually visible because the location is in + // a system header! + if (NumComponents != 1) + Diag(BuiltinLoc, diag::ext_offsetof_extended_field_designator) + << SourceRange(CompPtr[1].LocStart, CompPtr[NumComponents-1].LocEnd); + + bool DidWarnAboutNonPOD = false; + QualType CurrentType = ArgTy; + typedef OffsetOfExpr::OffsetOfNode OffsetOfNode; + llvm::SmallVector Comps; + llvm::SmallVector Exprs; + for (unsigned i = 0; i != NumComponents; ++i) { + const OffsetOfComponent &OC = CompPtr[i]; + if (OC.isBrackets) { + // Offset of an array sub-field. TODO: Should we allow vector elements? + if (!CurrentType->isDependentType()) { + const ArrayType *AT = Context.getAsArrayType(CurrentType); + if(!AT) + return ExprError(Diag(OC.LocEnd, diag::err_offsetof_array_type) + << CurrentType); + CurrentType = AT->getElementType(); + } else + CurrentType = Context.DependentTy; + + // The expression must be an integral expression. + // FIXME: An integral constant expression? + Expr *Idx = static_cast(OC.U.E); + if (!Idx->isTypeDependent() && !Idx->isValueDependent() && + !Idx->getType()->isIntegerType()) + return ExprError(Diag(Idx->getLocStart(), + diag::err_typecheck_subscript_not_integer) + << Idx->getSourceRange()); + + // Record this array index. + Comps.push_back(OffsetOfNode(OC.LocStart, Exprs.size(), OC.LocEnd)); + Exprs.push_back(Idx); + continue; + } + + // Offset of a field. + if (CurrentType->isDependentType()) { + // We have the offset of a field, but we can't look into the dependent + // type. Just record the identifier of the field. + Comps.push_back(OffsetOfNode(OC.LocStart, OC.U.IdentInfo, OC.LocEnd)); + CurrentType = Context.DependentTy; + continue; + } + + // We need to have a complete type to look into. + if (RequireCompleteType(OC.LocStart, CurrentType, + diag::err_offsetof_incomplete_type)) + return ExprError(); + + // Look for the designated field. + const RecordType *RC = CurrentType->getAs(); + if (!RC) + return ExprError(Diag(OC.LocEnd, diag::err_offsetof_record_type) + << CurrentType); + RecordDecl *RD = RC->getDecl(); + + // C++ [lib.support.types]p5: + // The macro offsetof accepts a restricted set of type arguments in this + // International Standard. type shall be a POD structure or a POD union + // (clause 9). + if (CXXRecordDecl *CRD = dyn_cast(RD)) { + if (!CRD->isPOD() && !DidWarnAboutNonPOD && + DiagRuntimeBehavior(BuiltinLoc, + PDiag(diag::warn_offsetof_non_pod_type) + << SourceRange(CompPtr[0].LocStart, OC.LocEnd) + << CurrentType)) + DidWarnAboutNonPOD = true; + } + + // Look for the field. + LookupResult R(*this, OC.U.IdentInfo, OC.LocStart, LookupMemberName); + LookupQualifiedName(R, RD); + FieldDecl *MemberDecl = R.getAsSingle(); + if (!MemberDecl) + return ExprError(Diag(BuiltinLoc, diag::err_no_member) + << OC.U.IdentInfo << RD << SourceRange(OC.LocStart, + OC.LocEnd)); + + // FIXME: C99 Verify that MemberDecl isn't a bitfield. + + if (cast(MemberDecl->getDeclContext())-> + isAnonymousStructOrUnion()) { + llvm::SmallVector Path; + BuildAnonymousStructUnionMemberPath(MemberDecl, Path); + unsigned n = Path.size(); + for (int j = n - 1; j > -1; --j) + Comps.push_back(OffsetOfNode(OC.LocStart, Path[j], OC.LocEnd)); + } else { + Comps.push_back(OffsetOfNode(OC.LocStart, MemberDecl, OC.LocEnd)); + } + CurrentType = MemberDecl->getType().getNonReferenceType(); + } + + return Owned(OffsetOfExpr::Create(Context, Context.getSizeType(), BuiltinLoc, + TInfo, Comps.data(), Comps.size(), + Exprs.data(), Exprs.size(), RParenLoc)); +} + Sema::OwningExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, SourceLocation BuiltinLoc, SourceLocation TypeLoc, @@ -6615,45 +6745,56 @@ Sema::OwningExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, OffsetOfComponent *CompPtr, unsigned NumComponents, SourceLocation RPLoc) { - // FIXME: This function leaks all expressions in the offset components on - // error. - // FIXME: Preserve type source info. - QualType ArgTy = GetTypeFromParser(argty); - assert(!ArgTy.isNull() && "Missing type argument!"); - bool Dependent = ArgTy->isDependentType(); + TypeSourceInfo *ArgTInfo; + QualType ArgTy = GetTypeFromParser(argty, &ArgTInfo); + if (ArgTy.isNull()) + return ExprError(); + if (getLangOptions().CPlusPlus) { + if (!ArgTInfo) + ArgTInfo = Context.getTrivialTypeSourceInfo(ArgTy, TypeLoc); + + return BuildBuiltinOffsetOf(BuiltinLoc, ArgTInfo, CompPtr, NumComponents, + RPLoc); + } + + // FIXME: The code below is marked for death, once we have proper CodeGen + // support for non-constant OffsetOf expressions. + + bool Dependent = ArgTy->isDependentType(); + // We must have at least one component that refers to the type, and the first // one is known to be a field designator. Verify that the ArgTy represents // a struct/union/class. if (!Dependent && !ArgTy->isRecordType()) return ExprError(Diag(TypeLoc, diag::err_offsetof_record_type) << ArgTy); - + // FIXME: Type must be complete per C99 7.17p3 because a declaring a variable // with an incomplete type would be illegal. - + // Otherwise, create a null pointer as the base, and iteratively process // the offsetof designators. QualType ArgTyPtr = Context.getPointerType(ArgTy); Expr* Res = new (Context) ImplicitValueInitExpr(ArgTyPtr); Res = new (Context) UnaryOperator(Res, UnaryOperator::Deref, ArgTy, SourceLocation()); - + // offsetof with non-identifier designators (e.g. "offsetof(x, a.b[c])") are a // GCC extension, diagnose them. // FIXME: This diagnostic isn't actually visible because the location is in // a system header! if (NumComponents != 1) Diag(BuiltinLoc, diag::ext_offsetof_extended_field_designator) - << SourceRange(CompPtr[1].LocStart, CompPtr[NumComponents-1].LocEnd); - + << SourceRange(CompPtr[1].LocStart, CompPtr[NumComponents-1].LocEnd); + if (!Dependent) { bool DidWarnAboutNonPOD = false; - + if (RequireCompleteType(TypeLoc, Res->getType(), diag::err_offsetof_incomplete_type)) return ExprError(); - + // FIXME: Dependent case loses a lot of information here. And probably // leaks like a sieve. for (unsigned i = 0; i != NumComponents; ++i) { @@ -6664,71 +6805,71 @@ Sema::OwningExprResult Sema::ActOnBuiltinOffsetOf(Scope *S, if (!AT) { Res->Destroy(Context); return ExprError(Diag(OC.LocEnd, diag::err_offsetof_array_type) - << Res->getType()); + << Res->getType()); } - + // FIXME: C++: Verify that operator[] isn't overloaded. - + // Promote the array so it looks more like a normal array subscript // expression. DefaultFunctionArrayLvalueConversion(Res); - + // C99 6.5.2.1p1 Expr *Idx = static_cast(OC.U.E); // FIXME: Leaks Res if (!Idx->isTypeDependent() && !Idx->getType()->isIntegerType()) return ExprError(Diag(Idx->getLocStart(), diag::err_typecheck_subscript_not_integer) - << Idx->getSourceRange()); - + << Idx->getSourceRange()); + Res = new (Context) ArraySubscriptExpr(Res, Idx, AT->getElementType(), OC.LocEnd); continue; } - + const RecordType *RC = Res->getType()->getAs(); if (!RC) { Res->Destroy(Context); return ExprError(Diag(OC.LocEnd, diag::err_offsetof_record_type) - << Res->getType()); + << Res->getType()); } - + // Get the decl corresponding to this. RecordDecl *RD = RC->getDecl(); if (CXXRecordDecl *CRD = dyn_cast(RD)) { if (!CRD->isPOD() && !DidWarnAboutNonPOD && DiagRuntimeBehavior(BuiltinLoc, PDiag(diag::warn_offsetof_non_pod_type) - << SourceRange(CompPtr[0].LocStart, OC.LocEnd) - << Res->getType())) + << SourceRange(CompPtr[0].LocStart, OC.LocEnd) + << Res->getType())) DidWarnAboutNonPOD = true; } - + LookupResult R(*this, OC.U.IdentInfo, OC.LocStart, LookupMemberName); LookupQualifiedName(R, RD); - + FieldDecl *MemberDecl = R.getAsSingle(); // FIXME: Leaks Res if (!MemberDecl) return ExprError(Diag(BuiltinLoc, diag::err_no_member) - << OC.U.IdentInfo << RD << SourceRange(OC.LocStart, OC.LocEnd)); - + << OC.U.IdentInfo << RD << SourceRange(OC.LocStart, OC.LocEnd)); + // FIXME: C++: Verify that MemberDecl isn't a static field. // FIXME: Verify that MemberDecl isn't a bitfield. if (cast(MemberDecl->getDeclContext())->isAnonymousStructOrUnion()) { Res = BuildAnonymousStructUnionMemberReference( - OC.LocEnd, MemberDecl, Res, OC.LocEnd).takeAs(); + OC.LocEnd, MemberDecl, Res, OC.LocEnd).takeAs(); } else { PerformObjectMemberConversion(Res, /*Qualifier=*/0, *R.begin(), MemberDecl); // MemberDecl->getType() doesn't get the right qualifiers, but it // doesn't matter here. Res = new (Context) MemberExpr(Res, false, MemberDecl, OC.LocEnd, - MemberDecl->getType().getNonReferenceType()); + MemberDecl->getType().getNonReferenceType()); } } } - + return Owned(new (Context) UnaryOperator(Res, UnaryOperator::OffsetOf, Context.getSizeType(), BuiltinLoc)); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index cca14caa4439..17f6553d0dcc 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1067,6 +1067,19 @@ public: return getSema().BuildUnaryOp(/*Scope=*/0, OpLoc, Opc, move(SubExpr)); } + /// \brief Build a new builtin offsetof expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + OwningExprResult RebuildOffsetOfExpr(SourceLocation OperatorLoc, + TypeSourceInfo *Type, + Action::OffsetOfComponent *Components, + unsigned NumComponents, + SourceLocation RParenLoc) { + return getSema().BuildBuiltinOffsetOf(OperatorLoc, Type, Components, + NumComponents, RParenLoc); + } + /// \brief Build a new sizeof or alignof expression with a type argument. /// /// By default, performs semantic analysis to build the new expression. @@ -4166,6 +4179,64 @@ TreeTransform::TransformUnaryOperator(UnaryOperator *E) { move(SubExpr)); } +template +Sema::OwningExprResult +TreeTransform::TransformOffsetOfExpr(OffsetOfExpr *E) { + // Transform the type. + TypeSourceInfo *Type = getDerived().TransformType(E->getTypeSourceInfo()); + if (!Type) + return getSema().ExprError(); + + // Transform all of the components into components similar to what the + // parser uses. + // FIXME: It would be slightly more efficient in the non-dependent case to + // just map FieldDecls, rather than requiring the rebuilder to look for + // the fields again. However, __builtin_offsetof is rare enough in + // template code that we don't care. + bool ExprChanged = false; + typedef Action::OffsetOfComponent Component; + typedef OffsetOfExpr::OffsetOfNode Node; + llvm::SmallVector Components; + for (unsigned I = 0, N = E->getNumComponents(); I != N; ++I) { + const Node &ON = E->getComponent(I); + Component Comp; + Comp.LocStart = ON.getRange().getBegin(); + Comp.LocEnd = ON.getRange().getEnd(); + switch (ON.getKind()) { + case Node::Array: { + Expr *FromIndex = E->getIndexExpr(ON.getArrayExprIndex()); + OwningExprResult Index = getDerived().TransformExpr(FromIndex); + if (Index.isInvalid()) + return getSema().ExprError(); + + ExprChanged = ExprChanged || Index.get() != FromIndex; + Comp.isBrackets = true; + Comp.U.E = Index.takeAs(); // FIXME: leaked + break; + } + + case Node::Field: + case Node::Identifier: + Comp.isBrackets = false; + Comp.U.IdentInfo = ON.getFieldName(); + break; + } + + Components.push_back(Comp); + } + + // If nothing changed, retain the existing expression. + if (!getDerived().AlwaysRebuild() && + Type == E->getTypeSourceInfo() && + !ExprChanged) + return SemaRef.Owned(E->Retain()); + + // Build a new offsetof expression. + return getDerived().RebuildOffsetOfExpr(E->getOperatorLoc(), Type, + Components.data(), Components.size(), + E->getRParenLoc()); +} + template Sema::OwningExprResult TreeTransform::TransformSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { diff --git a/clang/test/PCH/exprs.c b/clang/test/PCH/exprs.c index 2b588a229e76..038a18ba5b26 100644 --- a/clang/test/PCH/exprs.c +++ b/clang/test/PCH/exprs.c @@ -5,6 +5,7 @@ // RUN: %clang_cc1 -emit-pch -fblocks -o %t %S/exprs.h // RUN: %clang_cc1 -fblocks -include-pch %t -fsyntax-only -verify %s +__SIZE_TYPE__ size_type_value; int integer; long long_integer; double floating; @@ -35,6 +36,9 @@ char_literal *int_ptr3 = &integer; // UnaryOperator negate_enum *int_ptr4 = &integer; +// OffsetOfExpr +offsetof_type *offsetof_ptr = &size_type_value; + // SizeOfAlignOfExpr typeof(sizeof(float)) size_t_value; typeof_sizeof *size_t_ptr = &size_t_value; diff --git a/clang/test/PCH/exprs.h b/clang/test/PCH/exprs.h index 7012422aad33..5af8c7c1a900 100644 --- a/clang/test/PCH/exprs.h +++ b/clang/test/PCH/exprs.h @@ -25,6 +25,19 @@ typedef typeof('a') char_literal; // UnaryOperator typedef typeof(-Enumerator) negate_enum; +// OffsetOfExpr +struct X { + int member; +}; +struct Y { + struct X array[5]; +}; +struct Z { + struct Y y; +}; +typedef typeof(__builtin_offsetof(struct Z, y.array[1 + 2].member)) + offsetof_type; + // SizeOfAlignOfExpr typedef typeof(sizeof(int)) typeof_sizeof; typedef typeof(sizeof(Enumerator)) typeof_sizeof2; diff --git a/clang/test/SemaCXX/offsetof.cpp b/clang/test/SemaCXX/offsetof.cpp index 3283270847fd..a1d907ab360a 100644 --- a/clang/test/SemaCXX/offsetof.cpp +++ b/clang/test/SemaCXX/offsetof.cpp @@ -18,3 +18,13 @@ struct Derived : Base { int y; }; int o = __builtin_offsetof(Derived, x); // expected-warning{{offset of on non-POD type}} const int o2 = sizeof(__builtin_offsetof(Derived, x)); + +struct HasArray { + int array[17]; +}; + +// Constant and non-constant offsetof expressions +void test_ice(int i) { + int array0[__builtin_offsetof(HasArray, array[5])]; + int array1[__builtin_offsetof(HasArray, array[i])]; // expected-error{{variable length arrays are not permitted in C++}} +} diff --git a/clang/test/SemaTemplate/instantiate-expr-5.cpp b/clang/test/SemaTemplate/instantiate-expr-5.cpp index 941dae44829a..916c4f949311 100644 --- a/clang/test/SemaTemplate/instantiate-expr-5.cpp +++ b/clang/test/SemaTemplate/instantiate-expr-5.cpp @@ -1,4 +1,22 @@ -// RUN: %clang_cc1 -fsyntax-only %s +// RUN: %clang_cc1 -fsyntax-only -verify %s template int x(A x) { return x++; } int y() { return x(1); } + +namespace PR5880 { + template + struct A { + static const int a = __builtin_offsetof(T, a.array[5].m); // expected-error{{error: no member named 'a' in 'HasM'}} + }; + struct HasM { + float m; + }; + + struct ArrayOfHasM { + HasM array[10]; + }; + + struct B { ArrayOfHasM a; }; + A x; + A x2; // expected-note{{in instantiation of}} +} diff --git a/clang/tools/CIndex/CIndex.cpp b/clang/tools/CIndex/CIndex.cpp index 9cdb9659fe95..6221a615aa71 100644 --- a/clang/tools/CIndex/CIndex.cpp +++ b/clang/tools/CIndex/CIndex.cpp @@ -306,6 +306,7 @@ public: bool VisitExplicitCastExpr(ExplicitCastExpr *E); bool VisitObjCMessageExpr(ObjCMessageExpr *E); bool VisitObjCEncodeExpr(ObjCEncodeExpr *E); + bool VisitOffsetOfExpr(OffsetOfExpr *E); bool VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); }; @@ -943,6 +944,14 @@ bool CursorVisitor::VisitBlockExpr(BlockExpr *B) { return Visit(B->getBlockDecl()); } +bool CursorVisitor::VisitOffsetOfExpr(OffsetOfExpr *E) { + // FIXME: Visit fields as well? + if (Visit(E->getTypeSourceInfo()->getTypeLoc())) + return true; + + return VisitExpr(E); +} + bool CursorVisitor::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { if (E->isArgumentType()) { if (TypeSourceInfo *TSInfo = E->getArgumentTypeInfo()) diff --git a/clang/tools/CIndex/CXCursor.cpp b/clang/tools/CIndex/CXCursor.cpp index 3bc5d01fbada..c8eb4823779b 100644 --- a/clang/tools/CIndex/CXCursor.cpp +++ b/clang/tools/CIndex/CXCursor.cpp @@ -140,7 +140,8 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, ASTUnit *TU) { case Stmt::StringLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::ParenExprClass: - case Stmt::UnaryOperatorClass: + case Stmt::UnaryOperatorClass: + case Stmt::OffsetOfExprClass: case Stmt::SizeOfAlignOfExprClass: case Stmt::ArraySubscriptExprClass: case Stmt::BinaryOperatorClass: -- GitLab