ahatanak updated this revision to Diff 263753.
ahatanak marked 6 inline comments as done.
ahatanak added a comment.

Address review comments. Handle derived-to-base cast expressions and array 
subscript expressions that don't have constant indices.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D78767/new/

https://reviews.llvm.org/D78767

Files:
  clang/lib/Sema/SemaChecking.cpp
  clang/test/SemaCXX/warn-cast-align.cpp

Index: clang/test/SemaCXX/warn-cast-align.cpp
===================================================================
--- clang/test/SemaCXX/warn-cast-align.cpp
+++ clang/test/SemaCXX/warn-cast-align.cpp
@@ -43,3 +43,97 @@
   typedef int *IntPtr;
   c = IntPtr(P);
 }
+
+struct __attribute__((aligned(16))) A {
+  char m0[16];
+  char m1[16];
+};
+
+struct B0 {
+  char m0[16];
+};
+
+struct B1 {
+  char m0[16];
+};
+
+struct C {
+  A &m0;
+  B0 &m1;
+  A m2;
+};
+
+struct __attribute__((aligned(16))) D0 : B0, B1 {
+};
+
+struct __attribute__((aligned(16))) D1 : virtual B0 {
+};
+
+struct B2 {
+  char m0[8];
+};
+
+struct B3 {
+  char m0[8];
+};
+
+struct B4 {
+  char m0[8];
+};
+
+struct D2 : B2, B3 {
+};
+
+struct __attribute__((aligned(16))) D3 : B4, D2 {
+};
+
+struct __attribute__((aligned(16))) D4 : virtual D2 {
+};
+
+void test2(int n, A *a2) {
+  __attribute__((aligned(16))) char m[sizeof(A) * 2];
+  char(&m_ref)[sizeof(A) * 2] = m;
+  extern char(&m_ref_noinit)[sizeof(A) * 2];
+  __attribute__((aligned(16))) char vararray[10][n];
+  A t0;
+  B0 t1;
+  C t2 = {.m0 = t0, .m1 = t1};
+  __attribute__((aligned(16))) char t3[5][5][5];
+  __attribute__((aligned(16))) char t4[4][16];
+  D0 t5;
+  D1 t6;
+  D3 t7;
+  D4 t8;
+
+  A *a;
+  a = (A *)&m;
+  a = (A *)(m + sizeof(A));
+  a = (A *)(sizeof(A) + m);
+  a = (A *)((sizeof(A) * 2 + m) - sizeof(A));
+  a = (A *)((sizeof(A) * 2 + m) - 1); // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)(m + 1);                   // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)(1 + m);                   // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)(m + n);                   // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)&m[sizeof(A)];
+  a = (A *)&m[n]; // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)&m_ref;
+  a = (A *)&m_ref_noinit;        // expected-warning {{cast from 'char (*)[64]' to 'A *'}}
+  a = (A *)(&vararray[4][0]);    // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)(a2->m0 + sizeof(A)); // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)(&t2.m0);
+  a = (A *)(&t2.m1); // expected-warning {{cast from 'B0 *' to 'A *'}}
+  a = (A *)(&t2.m2);
+  a = (A *)(t2.m2.m1);
+  a = (A *)(&t3[3][3][0]); // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)(&t3[2][2][4]);
+  a = (A *)(&t3[0][n][0]); // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)&t4[n][0];
+  a = (A *)&t4[n][1]; // expected-warning {{cast from 'char *' to 'A *'}}
+  a = (A *)(static_cast<B1 *>(&t5));
+  a = (A *)(&(static_cast<B1 &>(t5)));
+  a = (A *)(static_cast<B0 *>(&t6)); // expected-warning {{cast from 'B0 *' to 'A *'}}
+  a = (A *)(static_cast<B2 *>(&t7)); // expected-warning {{cast from 'B2 *' to 'A *'}}
+  a = (A *)(static_cast<B3 *>(&t7));
+  a = (A *)(static_cast<B2 *>(&t8)); // expected-warning {{cast from 'B2 *' to 'A *'}}
+  a = (A *)(static_cast<B3 *>(&t8)); // expected-warning {{cast from 'B3 *' to 'A *'}}
+}
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -30,6 +30,7 @@
 #include "clang/AST/NSAPI.h"
 #include "clang/AST/NonTrivialTypeVisitor.h"
 #include "clang/AST/OperationKinds.h"
+#include "clang/AST/RecordLayout.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/Type.h"
@@ -13105,17 +13106,192 @@
   return HasInvalidParm;
 }
 
-/// A helper function to get the alignment of a Decl referred to by DeclRefExpr
-/// or MemberExpr.
-static CharUnits getDeclAlign(Expr *E, CharUnits TypeAlign,
-                              ASTContext &Context) {
-  if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
-    return Context.getDeclAlign(DRE->getDecl());
+Optional<std::pair<CharUnits, CharUnits>>
+static getBaseAlignmentAndOffsetFromPtr(const Expr *E, ASTContext &Ctx);
+
+/// Compute the alignment and offset of the base class object given the
+/// derived-to-base cast expression and the alignment and offset of the derived
+/// class object.
+static std::pair<CharUnits, CharUnits> getDerivedToBaseAlignmentAndOffset(
+    const ImplicitCastExpr *ICE, QualType DerivedType, CharUnits BaseAlignment,
+    CharUnits Offset, ASTContext &Ctx) {
+  for (auto PathI = ICE->path_begin(), PathE = ICE->path_end(); PathI != PathE;
+       ++PathI) {
+    const CXXBaseSpecifier *Base = *PathI;
+    if (Base->isVirtual()) {
+      BaseAlignment = Ctx.getTypeAlignInChars(Base->getType());
+      Offset = CharUnits::Zero();
+    } else {
+      const ASTRecordLayout &RL =
+          Ctx.getASTRecordLayout(DerivedType->getAsCXXRecordDecl());
+      Offset += RL.getBaseClassOffset(Base->getType()->getAsCXXRecordDecl());
+    }
+    DerivedType = Base->getType();
+  }
+
+  return std::make_pair(BaseAlignment, Offset);
+}
+
+/// This helper function takes an lvalue expression and returns the alignment of
+/// a VarDecl and a constant offset from the VarDecl.
+Optional<std::pair<CharUnits, CharUnits>>
+static getBaseAlignmentAndOffsetFromLValue(const Expr *E, ASTContext &Ctx) {
+  E = E->IgnoreParens();
+  switch (E->getStmtClass()) {
+  default:
+    break;
+  case Expr::CStyleCastExprClass:
+  case Expr::CXXStaticCastExprClass:
+    return getBaseAlignmentAndOffsetFromLValue(cast<CastExpr>(E)->getSubExpr(),
+                                               Ctx);
+  case Stmt::ImplicitCastExprClass: {
+    auto *ICE = cast<ImplicitCastExpr>(E);
+    const Expr *From = ICE->getSubExpr();
+    switch (ICE->getCastKind()) {
+    default:
+      break;
+    case CK_UncheckedDerivedToBase:
+    case CK_DerivedToBase: {
+      auto P = getBaseAlignmentAndOffsetFromLValue(From, Ctx);
+      if (!P)
+        break;
+      return getDerivedToBaseAlignmentAndOffset(ICE, From->getType(), P->first,
+                                                P->second, Ctx);
+    }
+    }
+    break;
+  }
+  case Stmt::ArraySubscriptExprClass: {
+    if (!E->getType()->isConstantSizeType())
+      break;
+    auto *ASE = cast<ArraySubscriptExpr>(E);
+    auto P = getBaseAlignmentAndOffsetFromPtr(ASE->getBase(), Ctx);
+    if (P) {
+      llvm::APSInt IdxRes;
+      CharUnits EltSize = Ctx.getTypeSizeInChars(E->getType());
+      if (ASE->getIdx()->isIntegerConstantExpr(IdxRes, Ctx))
+        return std::make_pair(P->first,
+                              P->second + EltSize * IdxRes.getExtValue());
+      // If the index isn't a constant expression, compute the lower bound of
+      // the alignment using the alignment and offset of the base and the
+      // element size.
+      return std::make_pair(
+          P->first.alignmentAtOffset(P->second).alignmentAtOffset(EltSize),
+          CharUnits::Zero());
+    }
+    break;
+  }
+  case Stmt::DeclRefExprClass: {
+    if (auto *VD = dyn_cast<VarDecl>(cast<DeclRefExpr>(E)->getDecl())) {
+      // FIXME: If VD is captured by copy or is an escaping __block variable,
+      // use the alignment of VD's type.
+      if (!VD->getType()->isReferenceType())
+        return std::make_pair(Ctx.getDeclAlign(VD), CharUnits::Zero());
+      if (VD->hasInit())
+        return getBaseAlignmentAndOffsetFromLValue(VD->getInit(), Ctx);
+    }
+    break;
+  }
+  case Stmt::MemberExprClass: {
+    auto *ME = cast<MemberExpr>(E);
+    if (ME->isArrow())
+      break;
+    auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
+    if (!FD || FD->getType()->isReferenceType())
+      break;
+    auto P = getBaseAlignmentAndOffsetFromLValue(ME->getBase(), Ctx);
+    if (!P)
+      break;
+    const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(FD->getParent());
+    uint64_t Offset = Layout.getFieldOffset(FD->getFieldIndex());
+    return std::make_pair(P->first,
+                          P->second + CharUnits::fromQuantity(Offset));
+  }
+  }
+  return Optional<std::pair<CharUnits, CharUnits>>();
+}
 
-  if (const auto *ME = dyn_cast<MemberExpr>(E))
-    return Context.getDeclAlign(ME->getMemberDecl());
+/// This helper function takes a pointer expression and returns the alignment of
+/// a VarDecl and a constant offset from the VarDecl.
+Optional<std::pair<CharUnits, CharUnits>>
+static getBaseAlignmentAndOffsetFromPtr(const Expr *E, ASTContext &Ctx) {
+  E = E->IgnoreParens();
+  switch (E->getStmtClass()) {
+  default:
+    break;
+  case Expr::CStyleCastExprClass:
+  case Expr::CXXStaticCastExprClass:
+    return getBaseAlignmentAndOffsetFromPtr(cast<CastExpr>(E)->getSubExpr(),
+                                            Ctx);
+  case Stmt::UnaryOperatorClass: {
+    auto *UO = cast<UnaryOperator>(E);
+    if (UO->getOpcode() == UO_AddrOf)
+      return getBaseAlignmentAndOffsetFromLValue(UO->getSubExpr(), Ctx);
+    break;
+  }
+  case Stmt::ImplicitCastExprClass: {
+    auto *ICE = cast<ImplicitCastExpr>(E);
+    const Expr *From = ICE->getSubExpr();
+    switch (ICE->getCastKind()) {
+    default:
+      break;
+    case CK_ArrayToPointerDecay:
+      return getBaseAlignmentAndOffsetFromLValue(From, Ctx);
+    case CK_UncheckedDerivedToBase:
+    case CK_DerivedToBase: {
+      auto P = getBaseAlignmentAndOffsetFromPtr(From, Ctx);
+      if (!P)
+        break;
+      return getDerivedToBaseAlignmentAndOffset(
+          ICE, From->getType()->getPointeeType(), P->first, P->second, Ctx);
+    }
+    }
+    break;
+  }
+  case Stmt::BinaryOperatorClass: {
+    auto HandleBinOp = [&](const Expr *LHS, const Expr *RHS,
+                           BinaryOperatorKind Kind) {
+      auto LHSRes = getBaseAlignmentAndOffsetFromPtr(LHS, Ctx);
+      if (!LHSRes) {
+        if (Kind == BO_Add) {
+          std::swap(LHS, RHS);
+          LHSRes = getBaseAlignmentAndOffsetFromPtr(LHS, Ctx);
+        }
+        if (!LHSRes)
+          return Optional<std::pair<CharUnits, CharUnits>>();
+      }
 
-  return TypeAlign;
+      llvm::APSInt RHSRes;
+      if (!RHS->isIntegerConstantExpr(RHSRes, Ctx))
+        return Optional<std::pair<CharUnits, CharUnits>>();
+      CharUnits Offset = LHSRes->second;
+      if (Kind == BO_Add)
+        Offset += CharUnits::fromQuantity(RHSRes.getExtValue());
+      else
+        Offset -= CharUnits::fromQuantity(RHSRes.getExtValue());
+      return Optional<std::pair<CharUnits, CharUnits>>(
+          std::make_pair(LHSRes->first, Offset));
+    };
+    auto *BO = cast<BinaryOperator>(E);
+    auto Opcode = BO->getOpcode();
+    if (Opcode == BO_Add || Opcode == BO_Sub)
+      return HandleBinOp(BO->getLHS(), BO->getRHS(), Opcode);
+    break;
+  }
+  }
+  return Optional<std::pair<CharUnits, CharUnits>>();
+}
+
+static CharUnits getPresumedAlignmentOfPointer(const Expr *E, Sema &S) {
+  // See if we can compute the alignment of a VarDecl and an offset from it.
+  Optional<std::pair<CharUnits, CharUnits>> P =
+      getBaseAlignmentAndOffsetFromPtr(E, S.Context);
+
+  if (P)
+    return P->first.alignmentAtOffset(P->second);
+
+  // If that failed, return the type's alignment.
+  return S.Context.getTypeAlignInChars(E->getType()->getPointeeType());
 }
 
 /// CheckCastAlign - Implements -Wcast-align, which warns when a
@@ -13151,15 +13327,7 @@
   // includes 'void'.
   if (SrcPointee->isIncompleteType()) return;
 
-  CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee);
-
-  if (auto *CE = dyn_cast<CastExpr>(Op)) {
-    if (CE->getCastKind() == CK_ArrayToPointerDecay)
-      SrcAlign = getDeclAlign(CE->getSubExpr(), SrcAlign, Context);
-  } else if (auto *UO = dyn_cast<UnaryOperator>(Op)) {
-    if (UO->getOpcode() == UO_AddrOf)
-      SrcAlign = getDeclAlign(UO->getSubExpr(), SrcAlign, Context);
-  }
+  CharUnits SrcAlign = getPresumedAlignmentOfPointer(Op, *this);
 
   if (SrcAlign >= DestAlign) return;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to