C11-Alignas vs. Clang -Wcast-Align

C11-Alignas vs. Clang -Wcast-Align


Ich habe also den folgenden minimierten C11-Code, der eine Struktur definiert, die ein uint16_t enthält (was bedeutet, dass die Struktur auf 2 Bytes ausgerichtet sein sollte), und ich möchte einen Zeichenpuffer in einen Zeiger auf diese Struktur umwandeln.


Bei allen aufgedrehten Warnungen beschwerte sich clang zu Recht darüber, dass die Ausrichtungsanforderungen der Struktur nicht erfüllt seien. Also habe ich einen C11 alignas hinzugefügt Bezeichner für den Puffer, um sicherzustellen, dass der Puffer ausreichend ausgerichtet ist, aber das hat den Klang nicht zum Schweigen gebracht.


Meine Frage ist:mache ich etwas falsch mit alignas ? Oder betrachtet die -Wcast-align-Diagnose nur den Typ der Argumente und nicht auch die manuell angegebene Ausrichtung? (Mir ist klar, dass ich einfach auf void* umwandeln kann um die Diagnose zum Schweigen zu bringen, aber da dieser Codeabschnitt portabel sein soll, möchte ich die Diagnose nicht umgehen, es sei denn, ich bin mir sicher, dass es sich um einen Fehlalarm handelt.)


#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;
}

Compileroptionen und Fehlermeldung:


$ 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;
^~~~~~~~~~~~~~~~~~~~~~~~~

Compiler-Version:


$ 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

Aktualisierung:
Es gibt keine Warnung, wenn ich den Puffer und seine Ausrichtung in eine Struktur verschiebe. Ich interpretiere dies als Hinweis darauf, dass Clang tatsächlich nur die Typen für diese Warnung betrachtet.


#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;
}

Antworten:


Aus der Clang-Quelle in SemaChecking.cpp:~7862 scheinen sie nur Typen zu betrachten, wie Sie sie erwähnen:


  CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee);
if (SrcAlign >= DestAlign) return;
// else warn...

Für mich sieht es so aus, als würde Clang sich auf eine Besetzung im C-Stil vorbereiten, die wiederum die Ausrichtung der Besetzung überprüft.


void CastOperation::CheckCStyleCast()
-> Kind = CastKind Sema::PrepareScalarCast(...);
-> if (Kind == CK_BitCast)
checkCastAlign();
void checkCastAlign() {
Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange);
}

Hier ist die Methode mit etwas mehr Kontext:


/// 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;
}