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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102542 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 8770bfd..308f6b4 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -549,6 +549,57 @@
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<IdentifierInfo *> (Data & ~(uintptr_t)Mask);
+}
+
MemberExpr *MemberExpr::Create(ASTContext &C, Expr *base, bool isarrow,
NestedNameSpecifier *qual,
SourceRange qualrange,
@@ -1891,7 +1942,7 @@
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 @@
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 @@
// 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<SizeOfAlignOfExpr>(E);
@@ -2702,6 +2754,15 @@
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<Stmt **> (reinterpret_cast<OffsetOfNode *> (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/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index a52cf6f..7233518 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/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 @@
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 @@
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 @@
return Success(Info.Ctx.getTypeSizeInChars(SrcTy).getQuantity(), E);
}
+bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *E) {
+ CharUnits Result;
+ unsigned n = E->getNumComponents();
+ OffsetOfExpr* OOE = const_cast<OffsetOfExpr*>(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<RecordType>();
+ 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 @@
// 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/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 3996528..b50bb0c 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/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 @@
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/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index 3a19ec2..3d528f3 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -261,6 +261,30 @@
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/lib/Checker/GRExprEngine.cpp b/lib/Checker/GRExprEngine.cpp
index c11a16f..67090b8 100644
--- a/lib/Checker/GRExprEngine.cpp
+++ b/lib/Checker/GRExprEngine.cpp
@@ -823,6 +823,10 @@
VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst);
break;
+ case Stmt::OffsetOfExprClass:
+ VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst);
+ break;
+
case Stmt::SizeOfAlignOfExprClass:
VisitSizeOfAlignOfExpr(cast<SizeOfAlignOfExpr>(S), Pred, Dst);
break;
@@ -2611,6 +2615,21 @@
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 @@
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/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index 842590b..9849688 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -133,6 +133,7 @@
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 @@
return Visit(E->getSubExpr());
}
Value *VisitUnaryOffsetOf(const UnaryOperator *E);
-
+
// C++
Value *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) {
return Visit(DAE->getExpr());
@@ -1030,6 +1031,21 @@
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/lib/Frontend/PCHReaderStmt.cpp b/lib/Frontend/PCHReaderStmt.cpp
index 5e08be1..8588c8a 100644
--- a/lib/Frontend/PCHReaderStmt.cpp
+++ b/lib/Frontend/PCHReaderStmt.cpp
@@ -71,6 +71,7 @@
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 @@
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<Node::Kind>(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<FieldDecl>(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<Expr>(StmtStack[StmtStack.size() - N + I]));
+
+ return E->getNumExpressions();
+}
+
unsigned PCHStmtReader::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) {
VisitExpr(E);
E->setSizeof(Record[Idx++]);
@@ -1105,6 +1144,12 @@
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/lib/Frontend/PCHWriterStmt.cpp b/lib/Frontend/PCHWriterStmt.cpp
index a0ea5c9..3d158a4 100644
--- a/lib/Frontend/PCHWriterStmt.cpp
+++ b/lib/Frontend/PCHWriterStmt.cpp
@@ -62,6 +62,7 @@
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 @@
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/lib/Frontend/StmtXML.cpp b/lib/Frontend/StmtXML.cpp
index ce474d3..21dc0ba 100644
--- a/lib/Frontend/StmtXML.cpp
+++ b/lib/Frontend/StmtXML.cpp
@@ -125,6 +125,7 @@
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 @@
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/lib/Sema/Sema.h b/lib/Sema/Sema.h
index fb65862..5508938 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -1986,6 +1986,11 @@
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/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 0373ca8..91a3cbe 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -1950,6 +1950,10 @@
return GetExprRange(C, UO->getSubExpr(), MaxWidth);
}
}
+
+ if (dyn_cast<OffsetOfExpr>(E)) {
+ IntRange::forType(C, E->getType());
+ }
FieldDecl *BitField = E->getBitField();
if (BitField) {
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index bdd1cfb..6d5749b 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/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 @@
case UnaryOperator::OffsetOf:
assert(false && "Invalid unary operator");
break;
-
+
case UnaryOperator::PreInc:
case UnaryOperator::PreDec:
case UnaryOperator::PostInc:
@@ -6608,37 +6610,30 @@
return Owned(new (Context) StmtExpr(Compound, Ty, LPLoc, RPLoc));
}
-Sema::OwningExprResult Sema::ActOnBuiltinOffsetOf(Scope *S,
- SourceLocation BuiltinLoc,
- SourceLocation TypeLoc,
- TypeTy *argty,
+Sema::OwningExprResult Sema::BuildBuiltinOffsetOf(SourceLocation BuiltinLoc,
+ TypeSourceInfo *TInfo,
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!");
-
+ 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(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());
-
+ 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
@@ -6646,14 +6641,160 @@
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<OffsetOfNode, 4> Comps;
+ llvm::SmallVector<Expr*, 4> 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<Expr*>(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<RecordType>();
+ 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<CXXRecordDecl>(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<FieldDecl>();
+ 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<RecordDecl>(MemberDecl->getDeclContext())->
+ isAnonymousStructOrUnion()) {
+ llvm::SmallVector<FieldDecl*, 4> 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,
+ TypeTy *argty,
+ OffsetOfComponent *CompPtr,
+ unsigned NumComponents,
+ SourceLocation RPLoc) {
+
+ 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);
+
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 @@
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<Expr*>(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<RecordType>();
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<CXXRecordDecl>(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<FieldDecl>();
// 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<RecordDecl>(MemberDecl->getDeclContext())->isAnonymousStructOrUnion()) {
Res = BuildAnonymousStructUnionMemberReference(
- OC.LocEnd, MemberDecl, Res, OC.LocEnd).takeAs<Expr>();
+ OC.LocEnd, MemberDecl, Res, OC.LocEnd).takeAs<Expr>();
} 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/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index cca14ca..17f6553 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -1067,6 +1067,19 @@
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.
@@ -4168,6 +4181,64 @@
template<typename Derived>
Sema::OwningExprResult
+TreeTransform<Derived>::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<Component, 4> 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<Expr>(); // 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<typename Derived>
+Sema::OwningExprResult
TreeTransform<Derived>::TransformSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) {
if (E->isArgumentType()) {
TypeSourceInfo *OldT = E->getArgumentTypeInfo();