C11 allineas vs clang -Wcast-align

C11 allineas vs clang -Wcast-align


Quindi ho il seguente codice C11 ridotto a icona che definisce uno struct contenente un uint16_t (il che significa che lo struct dovrebbe essere allineato a 2 byte) e voglio eseguire il cast di un buffer char su un puntatore a quello struct.


Con tutti gli avvertimenti alzati, clang si è giustamente lamentato del fatto che i requisiti di allineamento della struttura non sono soddisfatti. Quindi ho aggiunto un alignas C11 specificatore al buffer per assicurarsi che il buffer sia sufficientemente allineato, ma ciò non ha zittito il clang.


La mia domanda è:sto sbagliando qualcosa con alignas ? O è solo che la diagnostica -Wcast-align esamina solo il tipo di argomenti e non anche l'allineamento specificato manualmente? (Mi rendo conto che posso semplicemente trasmettere a void* per silenziare la diagnostica, ma poiché questo pezzo di codice dovrebbe essere portatile, non voglio eludere la diagnostica a meno che non sia certo che sia un falso positivo.)


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

Opzioni del compilatore e messaggio di errore:


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

Versione del compilatore:


$ 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

Aggiornamento:
Non viene visualizzato alcun avviso quando sposto il buffer e il relativo allineamento in una struttura. Interpreto questo come un suggerimento che clang guarda davvero solo i tipi per questo avviso.


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

Risposte:


Dalla fonte clang, in SemaChecking.cpp:~7862, sembra che stiano guardando solo tipi come hai menzionato:


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

Mi sembra che clang si stia preparando per un cast in stile c che a sua volta verificherà l'allineamento del cast.


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

Ecco il metodo con un po' più di contesto:


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