diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -704,6 +704,11 @@ bool tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx, unsigned Type) const; + /// Compute the alignment of an expression of a pointer type using the + /// alignment of a ValueDecl referenced in the expression. If it's not + /// possible to compute the alignment, return zero. + CharUnits getAlignmentFromDecl(ASTContext &Ctx) const; + /// Enumeration used to describe the kind of Null pointer constant /// returned from \c isNullPointerConstant(). enum NullPointerConstantKind { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14827,3 +14827,21 @@ EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); return tryEvaluateBuiltinObjectSize(this, Type, Info, Result); } + +CharUnits Expr::getAlignmentFromDecl(ASTContext &Ctx) const { + assert(getType()->getAs() && + "expression must be a pointer type"); + + Expr::EvalStatus Status; + EvalInfo Info(Ctx, Status, EvalInfo::EM_IgnoreSideEffects); + LValue Result; + PointerExprEvaluator Eval(Info, Result, false); + + if (!Eval.Visit(this) || !Result.Base) + return CharUnits::Zero(); + + if (auto *VD = Result.Base.dyn_cast()) + return Ctx.getDeclAlign(VD).alignmentAtOffset(Result.Offset); + + return CharUnits::Zero(); +} 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 @@ -13055,19 +13055,6 @@ 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()); - - if (const auto *ME = dyn_cast(E)) - return Context.getDeclAlign(ME->getMemberDecl()); - - return TypeAlign; -} - /// CheckCastAlign - Implements -Wcast-align, which warns when a /// pointer cast increases the alignment requirements. void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) { @@ -13101,15 +13088,13 @@ // includes 'void'. if (SrcPointee->isIncompleteType()) return; - CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee); + // Try to compute an accurate alignment using the alignment of the ValueDecls + // in the expression. + CharUnits SrcAlign = Op->getAlignmentFromDecl(Context); - 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); - } + // If that failed, just use the type's alignment. + if (SrcAlign.isZero()) + SrcAlign = Context.getTypeAlignInChars(SrcPointee); if (SrcAlign >= DestAlign) return; diff --git a/clang/test/Sema/warn-cast-align.c b/clang/test/Sema/warn-cast-align.c --- a/clang/test/Sema/warn-cast-align.c +++ b/clang/test/Sema/warn-cast-align.c @@ -67,3 +67,14 @@ FnTy test5(void) { return (FnTy)&func5; } + +void test6(int n) { + __attribute__((aligned(16))) char m[sizeof(struct A) * 2]; + struct A *a = (struct A *)&m; + a = (struct A *)(m + sizeof(struct A)); + a = (struct A *)(sizeof(struct A) + m); + a = (struct A *)(m + 1); // expected-warning {{cast from 'char *' to 'struct A *'}} + a = (struct A *)(1 + m); // expected-warning {{cast from 'char *' to 'struct A *'}} + a = (struct A *)(m + n); // expected-warning {{cast from 'char *' to 'struct A *'}} + a = (struct A *)&m[sizeof(struct A)]; +}