[clang] 7c37684 - [analyzer] Improve PlacementNewChecker

Gabor Marton via cfe-commits cfe-commits at lists.llvm.org
Thu May 14 06:51:51 PDT 2020


Author: Gabor Marton
Date: 2020-05-14T15:50:39+02:00
New Revision: 7c3768495e8c1599dc30986f7bd47d5e91f303f2

URL: https://github.com/llvm/llvm-project/commit/7c3768495e8c1599dc30986f7bd47d5e91f303f2
DIFF: https://github.com/llvm/llvm-project/commit/7c3768495e8c1599dc30986f7bd47d5e91f303f2.diff

LOG: [analyzer] Improve PlacementNewChecker

Summary:
1. Added insufficient storage check for arrays
2. Added align support check

Based on https://reviews.llvm.org/D76229

Reviewers: aaron.ballman, lebedev.ri, NoQ, martong

Reviewed By: martong

Subscribers: xazax.hun, baloghadamsoftware, szepet, rnkovacs, a.sidorin, mikhail.ramalho, Szelethus, donat.nagy, dkrupp, Charusso, ASDenysPetrov, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D76996

Patch by Karasev Nikita!

Added: 
    

Modified: 
    clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
    clang/test/Analysis/placement-new.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
index 43ed0ffb238d..fec9fb59b2eb 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckPlacementNew.cpp
@@ -25,22 +25,47 @@ class PlacementNewChecker : public Checker<check::PreStmt<CXXNewExpr>> {
   void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const;
 
 private:
+  bool checkPlaceCapacityIsSufficient(const CXXNewExpr *NE,
+                                      CheckerContext &C) const;
+
+  bool checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
+                                   CheckerContext &C) const;
+
   // Returns the size of the target in a placement new expression.
   // E.g. in "new (&s) long" it returns the size of `long`.
-  SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, ProgramStateRef State,
-                                CheckerContext &C) const;
+  SVal getExtentSizeOfNewTarget(const CXXNewExpr *NE, CheckerContext &C,
+                                bool &IsArray) const;
   // Returns the size of the place in a placement new expression.
   // E.g. in "new (&s) long" it returns the size of `s`.
-  SVal getExtentSizeOfPlace(const Expr *NE, ProgramStateRef State,
-                            CheckerContext &C) const;
-  BugType BT{this, "Insufficient storage for placement new",
-             categories::MemoryError};
+  SVal getExtentSizeOfPlace(const CXXNewExpr *NE, CheckerContext &C) const;
+
+  void emitBadAlignReport(const Expr *P, CheckerContext &C,
+                          unsigned AllocatedTAlign,
+                          unsigned StorageTAlign) const;
+  unsigned getStorageAlign(CheckerContext &C, const ValueDecl *VD) const;
+
+  void checkElementRegionAlign(const ElementRegion *R, CheckerContext &C,
+                               const Expr *P, unsigned AllocatedTAlign) const;
+
+  void checkFieldRegionAlign(const FieldRegion *R, CheckerContext &C,
+                             const Expr *P, unsigned AllocatedTAlign) const;
+
+  bool isVarRegionAlignedProperly(const VarRegion *R, CheckerContext &C,
+                                  const Expr *P,
+                                  unsigned AllocatedTAlign) const;
+
+  BugType SBT{this, "Insufficient storage for placement new",
+              categories::MemoryError};
+  BugType ABT{this, "Bad align storage for placement new",
+              categories::MemoryError};
 };
 } // namespace
 
-SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
-                                               ProgramStateRef State,
+SVal PlacementNewChecker::getExtentSizeOfPlace(const CXXNewExpr *NE,
                                                CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  const Expr *Place = NE->getPlacementArg(0);
+
   const MemRegion *MRegion = C.getSVal(Place).getAsRegion();
   if (!MRegion)
     return UnknownVal();
@@ -63,13 +88,16 @@ SVal PlacementNewChecker::getExtentSizeOfPlace(const Expr *Place,
 }
 
 SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
-                                                   ProgramStateRef State,
-                                                   CheckerContext &C) const {
+                                                   CheckerContext &C,
+                                                   bool &IsArray) const {
+  ProgramStateRef State = C.getState();
   SValBuilder &SvalBuilder = C.getSValBuilder();
   QualType ElementType = NE->getAllocatedType();
   ASTContext &AstContext = C.getASTContext();
   CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
+  IsArray = false;
   if (NE->isArray()) {
+    IsArray = true;
     const Expr *SizeExpr = *NE->getArraySize();
     SVal ElementCount = C.getSVal(SizeExpr);
     if (auto ElementCountNL = ElementCount.getAs<NonLoc>()) {
@@ -91,38 +119,212 @@ SVal PlacementNewChecker::getExtentSizeOfNewTarget(const CXXNewExpr *NE,
   return UnknownVal();
 }
 
-void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
-                                       CheckerContext &C) const {
-  // Check only the default placement new.
-  if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
-    return;
-  if (NE->getNumPlacementArgs() == 0)
-    return;
-
-  ProgramStateRef State = C.getState();
-  SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, State, C);
-  const Expr *Place = NE->getPlacementArg(0);
-  SVal SizeOfPlace = getExtentSizeOfPlace(Place, State, C);
+bool PlacementNewChecker::checkPlaceCapacityIsSufficient(
+    const CXXNewExpr *NE, CheckerContext &C) const {
+  bool IsArrayTypeAllocated;
+  SVal SizeOfTarget = getExtentSizeOfNewTarget(NE, C, IsArrayTypeAllocated);
+  SVal SizeOfPlace = getExtentSizeOfPlace(NE, C);
   const auto SizeOfTargetCI = SizeOfTarget.getAs<nonloc::ConcreteInt>();
   if (!SizeOfTargetCI)
-    return;
+    return true;
   const auto SizeOfPlaceCI = SizeOfPlace.getAs<nonloc::ConcreteInt>();
   if (!SizeOfPlaceCI)
-    return;
+    return true;
 
-  if (SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) {
-    if (ExplodedNode *N = C.generateErrorNode(State)) {
-      std::string Msg = std::string(
-          llvm::formatv("Storage provided to placement new is only {0} bytes, "
-                        "whereas the allocated type requires {1} bytes",
-                        SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
+  if ((SizeOfPlaceCI->getValue() < SizeOfTargetCI->getValue()) ||
+      (IsArrayTypeAllocated &&
+       SizeOfPlaceCI->getValue() >= SizeOfTargetCI->getValue())) {
+    if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
+      std::string Msg;
+      // TODO: use clang constant
+      if (IsArrayTypeAllocated &&
+          SizeOfPlaceCI->getValue() > SizeOfTargetCI->getValue())
+        Msg = std::string(llvm::formatv(
+            "{0} bytes is possibly not enough for array allocation which "
+            "requires {1} bytes. Current overhead requires the size of {2} "
+            "bytes",
+            SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue(),
+            SizeOfPlaceCI->getValue() - SizeOfTargetCI->getValue()));
+      else if (IsArrayTypeAllocated &&
+               SizeOfPlaceCI->getValue() == SizeOfTargetCI->getValue())
+        Msg = std::string(llvm::formatv(
+            "Storage provided to placement new is only {0} bytes, "
+            "whereas the allocated array type requires more space for "
+            "internal needs",
+            SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
+      else
+        Msg = std::string(llvm::formatv(
+            "Storage provided to placement new is only {0} bytes, "
+            "whereas the allocated type requires {1} bytes",
+            SizeOfPlaceCI->getValue(), SizeOfTargetCI->getValue()));
 
-      auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
-      bugreporter::trackExpressionValue(N, Place, *R);
+      auto R = std::make_unique<PathSensitiveBugReport>(SBT, Msg, N);
+      bugreporter::trackExpressionValue(N, NE->getPlacementArg(0), *R);
       C.emitReport(std::move(R));
+
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void PlacementNewChecker::emitBadAlignReport(const Expr *P, CheckerContext &C,
+                                             unsigned AllocatedTAlign,
+                                             unsigned StorageTAlign) const {
+  ProgramStateRef State = C.getState();
+  if (ExplodedNode *N = C.generateErrorNode(State)) {
+    std::string Msg(llvm::formatv("Storage type is aligned to {0} bytes but "
+                                  "allocated type is aligned to {1} bytes",
+                                  StorageTAlign, AllocatedTAlign));
+
+    auto R = std::make_unique<PathSensitiveBugReport>(ABT, Msg, N);
+    bugreporter::trackExpressionValue(N, P, *R);
+    C.emitReport(std::move(R));
+  }
+}
+
+unsigned PlacementNewChecker::getStorageAlign(CheckerContext &C,
+                                              const ValueDecl *VD) const {
+  unsigned StorageTAlign = C.getASTContext().getTypeAlign(VD->getType());
+  if (unsigned SpecifiedAlignment = VD->getMaxAlignment())
+    StorageTAlign = SpecifiedAlignment;
+
+  return StorageTAlign / C.getASTContext().getCharWidth();
+}
+
+void PlacementNewChecker::checkElementRegionAlign(
+    const ElementRegion *R, CheckerContext &C, const Expr *P,
+    unsigned AllocatedTAlign) const {
+  auto IsBaseRegionAlignedProperly = [this, R, &C, P,
+                                      AllocatedTAlign]() -> bool {
+    // Unwind nested ElementRegion`s to get the type.
+    const MemRegion *SuperRegion = R;
+    while (true) {
+      if (SuperRegion->getKind() == MemRegion::ElementRegionKind) {
+        SuperRegion = cast<SubRegion>(SuperRegion)->getSuperRegion();
+        continue;
+      }
+
+      break;
+    }
+
+    const DeclRegion *TheElementDeclRegion = SuperRegion->getAs<DeclRegion>();
+    if (!TheElementDeclRegion)
+      return false;
+
+    const DeclRegion *BaseDeclRegion = R->getBaseRegion()->getAs<DeclRegion>();
+    if (!BaseDeclRegion)
+      return false;
+
+    unsigned BaseRegionAlign = 0;
+    // We must use alignment TheElementDeclRegion if it has its own alignment
+    // specifier
+    if (TheElementDeclRegion->getDecl()->getMaxAlignment())
+      BaseRegionAlign = getStorageAlign(C, TheElementDeclRegion->getDecl());
+    else
+      BaseRegionAlign = getStorageAlign(C, BaseDeclRegion->getDecl());
+
+    if (AllocatedTAlign > BaseRegionAlign) {
+      emitBadAlignReport(P, C, AllocatedTAlign, BaseRegionAlign);
+      return false;
+    }
+
+    return true;
+  };
+
+  auto CheckElementRegionOffset = [this, R, &C, P, AllocatedTAlign]() -> void {
+    RegionOffset TheOffsetRegion = R->getAsOffset();
+    if (TheOffsetRegion.hasSymbolicOffset())
+      return;
+
+    unsigned Offset =
+        TheOffsetRegion.getOffset() / C.getASTContext().getCharWidth();
+    unsigned AddressAlign = Offset % AllocatedTAlign;
+    if (AddressAlign != 0) {
+      emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
       return;
     }
+  };
+
+  if (IsBaseRegionAlignedProperly()) {
+    CheckElementRegionOffset();
+  }
+}
+
+void PlacementNewChecker::checkFieldRegionAlign(
+    const FieldRegion *R, CheckerContext &C, const Expr *P,
+    unsigned AllocatedTAlign) const {
+  const MemRegion *BaseRegion = R->getBaseRegion();
+  if (!BaseRegion)
+    return;
+
+  if (const VarRegion *TheVarRegion = BaseRegion->getAs<VarRegion>()) {
+    if (isVarRegionAlignedProperly(TheVarRegion, C, P, AllocatedTAlign)) {
+      // We've checked type align but, unless FieldRegion
+      // offset is zero, we also need to check its own
+      // align.
+      RegionOffset Offset = R->getAsOffset();
+      if (Offset.hasSymbolicOffset())
+        return;
+
+      int64_t OffsetValue =
+          Offset.getOffset() / C.getASTContext().getCharWidth();
+      unsigned AddressAlign = OffsetValue % AllocatedTAlign;
+      if (AddressAlign != 0)
+        emitBadAlignReport(P, C, AllocatedTAlign, AddressAlign);
+    }
+  }
+}
+
+bool PlacementNewChecker::isVarRegionAlignedProperly(
+    const VarRegion *R, CheckerContext &C, const Expr *P,
+    unsigned AllocatedTAlign) const {
+  const VarDecl *TheVarDecl = R->getDecl();
+  unsigned StorageTAlign = getStorageAlign(C, TheVarDecl);
+  if (AllocatedTAlign > StorageTAlign) {
+    emitBadAlignReport(P, C, AllocatedTAlign, StorageTAlign);
+
+    return false;
+  }
+
+  return true;
+}
+
+bool PlacementNewChecker::checkPlaceIsAlignedProperly(const CXXNewExpr *NE,
+                                                      CheckerContext &C) const {
+  const Expr *Place = NE->getPlacementArg(0);
+
+  QualType AllocatedT = NE->getAllocatedType();
+  unsigned AllocatedTAlign = C.getASTContext().getTypeAlign(AllocatedT) /
+                             C.getASTContext().getCharWidth();
+
+  SVal PlaceVal = C.getSVal(Place);
+  if (const MemRegion *MRegion = PlaceVal.getAsRegion()) {
+    if (const ElementRegion *TheElementRegion = MRegion->getAs<ElementRegion>())
+      checkElementRegionAlign(TheElementRegion, C, Place, AllocatedTAlign);
+    else if (const FieldRegion *TheFieldRegion = MRegion->getAs<FieldRegion>())
+      checkFieldRegionAlign(TheFieldRegion, C, Place, AllocatedTAlign);
+    else if (const VarRegion *TheVarRegion = MRegion->getAs<VarRegion>())
+      isVarRegionAlignedProperly(TheVarRegion, C, Place, AllocatedTAlign);
   }
+
+  return true;
+}
+
+void PlacementNewChecker::checkPreStmt(const CXXNewExpr *NE,
+                                       CheckerContext &C) const {
+  // Check only the default placement new.
+  if (!NE->getOperatorNew()->isReservedGlobalPlacementOperator())
+    return;
+
+  if (NE->getNumPlacementArgs() == 0)
+    return;
+
+  if (!checkPlaceCapacityIsSufficient(NE, C))
+    return;
+
+  checkPlaceIsAlignedProperly(NE, C);
 }
 
 void ento::registerPlacementNewChecker(CheckerManager &mgr) {

diff  --git a/clang/test/Analysis/placement-new.cpp b/clang/test/Analysis/placement-new.cpp
index 37102b810d98..f3ecd7ebf2a7 100644
--- a/clang/test/Analysis/placement-new.cpp
+++ b/clang/test/Analysis/placement-new.cpp
@@ -155,3 +155,309 @@ void f() {
   (void)dp;
 }
 } // namespace testHierarchy
+
+namespace testArrayTypesAllocation {
+void f1() {
+  struct S {
+    short a;
+  };
+
+  // bad (not enough space).
+  const unsigned N = 32;
+  alignas(S) unsigned char buffer1[sizeof(S) * N]; // expected-note {{'buffer1' initialized here}}
+  ::new (buffer1) S[N];                            // expected-warning{{Storage provided to placement new is only 64 bytes, whereas the allocated array type requires more space for internal needs}} expected-note 1 {{}}
+}
+
+void f2() {
+  struct S {
+    short a;
+  };
+
+  // maybe ok but we need to warn.
+  const unsigned N = 32;
+  alignas(S) unsigned char buffer2[sizeof(S) * N + sizeof(int)]; // expected-note {{'buffer2' initialized here}}
+  ::new (buffer2) S[N];                                          // expected-warning{{68 bytes is possibly not enough for array allocation which requires 64 bytes. Current overhead requires the size of 4 bytes}} expected-note 1 {{}}
+}
+} // namespace testArrayTypesAllocation
+
+namespace testStructAlign {
+void f1() {
+  struct X {
+    char a[9];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // bad (struct X is aligned to char).
+  ::new (&Xi.a) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f2() {
+  struct X {
+    char a;
+    char b;
+    long c;
+  } Xi;
+
+  // ok (struct X is aligned to long).
+  ::new (&Xi.a) long;
+}
+
+void f3() {
+  struct X {
+    char a;
+    char b;
+    long c;
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // bad (struct X is aligned to long but field 'b' is aligned to 1 because of its offset)
+  ::new (&Xi.b) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f4() {
+  struct X {
+    char a;
+    struct alignas(alignof(short)) Y {
+      char b;
+      char c;
+    } y;
+    long d;
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // bad. 'b' is aligned to short
+  ::new (&Xi.y.b) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f5() {
+  short b[10]; // expected-note {{'b' initialized here}}
+
+  ::new (&b) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f6() {
+  short b[10]; // expected-note {{'b' initialized here}}
+
+  // bad (same as previous but checks ElementRegion case)
+  ::new (&b[0]) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f7() {
+  alignas(alignof(long)) short b[10];
+
+  // ok. aligned to long(ok). offset 4*2(ok)
+  ::new (&b[4]) long;
+}
+
+void f8() {
+  alignas(alignof(long)) short b[10]; // expected-note {{'b' initialized here}}
+
+  // ok. aligned to long(ok). offset 3*2(ok)
+  ::new (&b[3]) long; // expected-warning{{Storage type is aligned to 6 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f9() {
+  struct X {
+    char a;
+    alignas(alignof(long)) char b[20];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // ok. aligned to long(ok). offset 8*1(ok)
+  ::new (&Xi.b[8]) long;
+
+  // bad. aligned to long(ok). offset 1*1(ok)
+  ::new (&Xi.b[1]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f10() {
+  struct X {
+    char a[8];
+    alignas(2) char b;
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // bad (struct X is aligned to 2).
+  ::new (&Xi.a) long; // expected-warning{{Storage type is aligned to 2 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void f11() {
+  struct X {
+    char a;
+    char b;
+    struct Y {
+      long c;
+    } d;
+  } Xi;
+
+  // ok (struct X is aligned to long).
+  ::new (&Xi.a) long;
+}
+
+void f12() {
+  struct alignas(alignof(long)) X {
+    char a;
+    char b;
+  } Xi;
+
+  // ok (struct X is aligned to long).
+  ::new (&Xi.a) long;
+}
+
+void test13() {
+  struct Y {
+    char a[10];
+  };
+
+  struct X {
+    Y b[10];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // bad. X,A are aligned to 'char'
+  ::new (&Xi.b[0].a) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void test14() {
+  struct Y {
+    char a[10];
+  };
+
+  struct alignas(alignof(long)) X {
+    Y b[10];
+  } Xi;
+
+  // ok. X is aligned to 'long' and field 'a' goes with zero offset
+  ::new (&Xi.b[0].a) long;
+}
+
+void test15() {
+  struct alignas(alignof(long)) Y {
+    char a[10];
+  };
+
+  struct X {
+    Y b[10];
+  } Xi;
+
+  // ok. X is aligned to 'long' because it contains struct 'Y' which is aligned to 'long'
+  ::new (&Xi.b[0].a) long;
+}
+
+void test16() {
+  struct alignas(alignof(long)) Y {
+    char p;
+    char a[10];
+  };
+
+  struct X {
+    Y b[10];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // bad. aligned to long(ok). offset 1(bad)
+  ::new (&Xi.b[0].a) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void test17() {
+  struct alignas(alignof(long)) Y {
+    char p;
+    char a[10];
+  };
+
+  struct X {
+    Y b[10];
+  } Xi;
+
+  // ok. aligned to long(ok). offset 1+7*1(ok)
+  ::new (&Xi.b[0].a[7]) long;
+}
+
+void test18() {
+  struct Y {
+    char p;
+    alignas(alignof(long)) char a[10];
+  };
+
+  struct X {
+    Y b[10];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // ok. aligned to long(ok). offset 8*1(ok)
+  ::new (&Xi.b[0].a[8]) long;
+
+  // bad. aligned to long(ok). offset 1(bad)
+  ::new (&Xi.b[0].a[1]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void test19() {
+  struct Z {
+    char p;
+    char c[10];
+  };
+
+  struct Y {
+    char p;
+    Z b[10];
+  };
+
+  struct X {
+    Y a[10];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // bad. all structures X,Y,Z are aligned to char
+  ::new (&Xi.a[1].b[1].c) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void test20() {
+  struct Z {
+    char p;
+    alignas(alignof(long)) char c[10];
+  };
+
+  struct Y {
+    char p;
+    Z b[10];
+  };
+
+  struct X {
+    Y a[10];
+  } Xi;
+
+  // ok. field 'c' is aligned to 'long'
+  ::new (&Xi.a[1].b[1].c) long;
+}
+
+void test21() {
+  struct Z {
+    char p;
+    char c[10];
+  };
+
+  struct Y {
+    char p;
+    Z b[10];
+  };
+
+  struct alignas(alignof(long)) X {
+    Y a[10];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // ok. aligned to long(ok). offset 1+7*1(ok)
+  ::new (&Xi.a[0].b[0].c[7]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+void test22() {
+  struct alignas(alignof(long)) Y {
+    char p;
+    char a[10][10];
+  };
+
+  struct X {
+    Y b[10];
+  } Xi; // expected-note {{'Xi' initialized here}}
+
+  // ok. aligned to long(ok). offset ok. 1(field 'a' offset) + 0*10(index '0' * first dimension size '10') + 7*1(index '7')
+  ::new (&Xi.b[0].a[0][7]) long;
+
+  // ok. aligned to long(ok). offset ok. 1(field 'a' offset) + 1*10(index '1' * first dimension size '10') + 5*1(index '5')
+  ::new (&Xi.b[0].a[1][5]) long;
+
+  // bad. aligned to long(ok). offset ok. 1(field 'a' offset) + 1*10(index '1' * first dimension size '10') + 6*1(index '5')
+  ::new (&Xi.b[0].a[1][6]) long; // expected-warning{{Storage type is aligned to 1 bytes but allocated type is aligned to 8 bytes}} expected-note 1 {{}}
+}
+
+} // namespace testStructAlign


        


More information about the cfe-commits mailing list