diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/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(E)) - return Context.getDeclAlign(DRE->getDecl()); +Optional> +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 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> +static getBaseAlignmentAndOffsetFromLValue(const Expr *E, ASTContext &Ctx) { + E = E->IgnoreParens(); + switch (E->getStmtClass()) { + default: + break; + case Expr::CStyleCastExprClass: + case Expr::CXXStaticCastExprClass: + return getBaseAlignmentAndOffsetFromLValue(cast(E)->getSubExpr(), + Ctx); + case Stmt::ImplicitCastExprClass: { + auto *ICE = cast(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(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(cast(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(E); + if (ME->isArrow()) + break; + auto *FD = dyn_cast(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>(); +} - if (const auto *ME = dyn_cast(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> +static getBaseAlignmentAndOffsetFromPtr(const Expr *E, ASTContext &Ctx) { + E = E->IgnoreParens(); + switch (E->getStmtClass()) { + default: + break; + case Expr::CStyleCastExprClass: + case Expr::CXXStaticCastExprClass: + return getBaseAlignmentAndOffsetFromPtr(cast(E)->getSubExpr(), + Ctx); + case Stmt::UnaryOperatorClass: { + auto *UO = cast(E); + if (UO->getOpcode() == UO_AddrOf) + return getBaseAlignmentAndOffsetFromLValue(UO->getSubExpr(), Ctx); + break; + } + case Stmt::ImplicitCastExprClass: { + auto *ICE = cast(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>(); + } - return TypeAlign; + llvm::APSInt RHSRes; + if (!RHS->isIntegerConstantExpr(RHSRes, Ctx)) + return Optional>(); + CharUnits Offset = LHSRes->second; + if (Kind == BO_Add) + Offset += CharUnits::fromQuantity(RHSRes.getExtValue()); + else + Offset -= CharUnits::fromQuantity(RHSRes.getExtValue()); + return Optional>( + std::make_pair(LHSRes->first, Offset)); + }; + auto *BO = cast(E); + auto Opcode = BO->getOpcode(); + if (Opcode == BO_Add || Opcode == BO_Sub) + return HandleBinOp(BO->getLHS(), BO->getRHS(), Opcode); + break; + } + } + return Optional>(); +} + +static CharUnits getPresumedAlignmentOfPointer(const Expr *E, Sema &S) { + // See if we can compute the alignment of a VarDecl and an offset from it. + Optional> 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(Op)) { - if (CE->getCastKind() == CK_ArrayToPointerDecay) - SrcAlign = getDeclAlign(CE->getSubExpr(), SrcAlign, Context); - } else if (auto *UO = dyn_cast(Op)) { - if (UO->getOpcode() == UO_AddrOf) - SrcAlign = getDeclAlign(UO->getSubExpr(), SrcAlign, Context); - } + CharUnits SrcAlign = getPresumedAlignmentOfPointer(Op, *this); if (SrcAlign >= DestAlign) return; diff --git a/clang/test/SemaCXX/warn-cast-align.cpp b/clang/test/SemaCXX/warn-cast-align.cpp --- a/clang/test/SemaCXX/warn-cast-align.cpp +++ b/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(&t5)); + a = (A *)(&(static_cast(t5))); + a = (A *)(static_cast(&t6)); // expected-warning {{cast from 'B0 *' to 'A *'}} + a = (A *)(static_cast(&t7)); // expected-warning {{cast from 'B2 *' to 'A *'}} + a = (A *)(static_cast(&t7)); + a = (A *)(static_cast(&t8)); // expected-warning {{cast from 'B2 *' to 'A *'}} + a = (A *)(static_cast(&t8)); // expected-warning {{cast from 'B3 *' to 'A *'}} +}