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());