そこで、uint16_t (つまり、構造体は 2 バイトに揃える必要がある) を含む構造体を定義する次の最小化された C11 コードがあり、char バッファをその構造体へのポインタにキャストしたいと考えています。
警告がすべてオンになっていると、clang は構造体のアラインメント要件が満たされていないと正しく警告しました。そこで、alignas
バッファが十分にアラインメントされていることを確認するために、バッファに C11 指定子を追加しましたが、それでも clang は黙りませんでした。
私の質問は、 で何か間違ったことをしているのでしょうかalignas
? それとも、 -Wcast-align 診断が引数の型のみを調べていて、手動で指定されたアラインメントも調べていないだけなのでしょうか? ( にキャストするだけでvoid*
診断を無効にできることはわかっていますが、このコードは移植可能であるはずなので、誤検知であることが確実でない限り、診断を回避したくありません。)
#include <stdint.h>
#include <stdalign.h>
struct foo {
uint16_t field1;
};
int main(void) {
alignas(struct foo) char buffer[122] = {0};
struct foo *foo = (struct foo*)buffer;
return foo->field1;
}
コンパイラ オプションとエラー メッセージ:
$ clang -ggdb -O3 foo.c -Weverything -Werror -Wno-c++98-compat -Wno-c11-extensions
foo.c:11:23: error: cast from 'char *' to 'struct foo *' increases required alignment from 1 to 2 [-Werror,-Wcast-align]
struct foo *foo = (struct foo*)buffer;
^~~~~~~~~~~~~~~~~~~~~~~~~
コンパイラバージョン:
$ clang -v
clang version 3.5.1 (tags/RELEASE_351/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
Selected GCC installation: /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4
アップデート:バッファとその配置を構造体に移動しても警告は出ません。これは、clang が実際にこの警告の型のみを参照しているというヒントだと解釈しています。
#include <stdint.h>
#include <stdalign.h>
struct foo {
uint16_t field1;
};
struct bar {
alignas(struct foo) char buffer[122];
};
int main(void) {
struct bar bar = {{0}};
struct foo *foo = (struct foo*)&bar;
return foo->field1;
}
ベストアンサー1
clang ソースの SemaChecking.cpp:~7862 では、あなたが言及したような型のみを参照しているようです:
CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee);
if (SrcAlign >= DestAlign) return;
// else warn...
clang は c スタイルのキャストを準備していて、それがキャストの配置をチェックするように見えます。
void CastOperation::CheckCStyleCast()
-> Kind = CastKind Sema::PrepareScalarCast(...);
-> if (Kind == CK_BitCast)
checkCastAlign();
void checkCastAlign() {
Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange);
}
以下に、もう少し詳しいコンテキストを含むメソッドを示します。
/// CheckCastAlign - Implements -Wcast-align, which warns when a
/// pointer cast increases the alignment requirements.
void Sema::CheckCastAlign(Expr *Op, QualType T, SourceRange TRange) {
// This is actually a lot of work to potentially be doing on every
// cast; don't do it if we're ignoring -Wcast_align (as is the default).
if (getDiagnostics().isIgnored(diag::warn_cast_align, TRange.getBegin()))
return;
// Ignore dependent types.
if (T->isDependentType() || Op->getType()->isDependentType())
return;
// Require that the destination be a pointer type.
const PointerType *DestPtr = T->getAs<PointerType>();
if (!DestPtr) return;
// If the destination has alignment 1, we're done.
QualType DestPointee = DestPtr->getPointeeType();
if (DestPointee->isIncompleteType()) return;
CharUnits DestAlign = Context.getTypeAlignInChars(DestPointee);
if (DestAlign.isOne()) return;
// Require that the source be a pointer type.
const PointerType *SrcPtr = Op->getType()->getAs<PointerType>();
if (!SrcPtr) return;
QualType SrcPointee = SrcPtr->getPointeeType();
// Whitelist casts from cv void*. We already implicitly
// whitelisted casts to cv void*, since they have alignment 1.
// Also whitelist casts involving incomplete types, which implicitly
// includes 'void'.
if (SrcPointee->isIncompleteType()) return;
CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee);
if (SrcAlign >= DestAlign) return;
Diag(TRange.getBegin(), diag::warn_cast_align)
<< Op->getType() << T
<< static_cast<unsigned>(SrcAlign.getQuantity())
<< static_cast<unsigned>(DestAlign.getQuantity())
<< TRange << Op->getSourceRange();
}
static const Type* getElementType(const Expr *BaseExpr) {
const Type* EltType = BaseExpr->getType().getTypePtr();
if (EltType->isAnyPointerType())
return EltType->getPointeeType().getTypePtr();
else if (EltType->isArrayType())
return EltType->getBaseElementTypeUnsafe();
return EltType;
}