]> granicus.if.org Git - clang/commitdiff
Teach __builtin_offsetof to compute the offsets of members of base
authorDouglas Gregor <dgregor@apple.com>
Thu, 29 Apr 2010 00:18:15 +0000 (00:18 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 29 Apr 2010 00:18:15 +0000 (00:18 +0000)
classes, since we only warn (not error) on offsetof() for non-POD
types. We store the base path within the OffsetOfExpr itself, then
evaluate the offsets within the constant evaluator.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102571 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Expr.h
lib/AST/ExprConstant.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtProfile.cpp
lib/Frontend/PCHReaderStmt.cpp
lib/Frontend/PCHWriterStmt.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/TreeTransform.h
test/SemaCXX/offsetof.cpp

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