C11 alignas vs. clang-Wcast-align
memory-alignment (1)
Así que tengo el siguiente código C11 minimizado que define una estructura que contiene un uint16_t (lo que significa que la estructura debería estar alineada a 2 bytes) y quiero convertir un búfer de caracteres en un puntero a esa estructura.
Con las advertencias activadas, Clang se quejó con razón de que no se cumplen los requisitos de alineación de la estructura. Así que agregué un especificador de alignas
C11 al búfer para asegurarme de que el búfer esté lo suficientemente alineado, pero eso no cerró el ruido.
Mi pregunta es: ¿estoy haciendo algo mal con alignas
? ¿O es solo que el diagnóstico de -Wcast-align solo está mirando el tipo de argumentos y no también la alineación manualmente especificada? (Me doy cuenta de que puedo void*
para silenciar el diagnóstico, pero como se supone que esta pieza de código es portátil, no quiero dejar de lado el diagnóstico a menos que esté seguro de que es 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;
}
Opciones del compilador y mensaje de error:
$ 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;
^~~~~~~~~~~~~~~~~~~~~~~~~
Versión del compilador:
$ 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
Actualización: No hay ninguna advertencia cuando muevo el búfer y su alineación en una estructura. Interpreto esto como un indicio de que Clang en efecto solo mira los tipos de esta advertencia.
#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;
}
Desde el código fuente de Clang, en SemaChecking.cpp: ~ 7862, parece que solo están mirando tipos como usted menciona:
CharUnits SrcAlign = Context.getTypeAlignInChars(SrcPointee);
if (SrcAlign >= DestAlign) return;
// else warn...
Me parece que Clang se está preparando para un elenco de estilo c que a su vez verificará la alineación del molde.
void CastOperation::CheckCStyleCast()
-> Kind = CastKind Sema::PrepareScalarCast(...);
-> if (Kind == CK_BitCast)
checkCastAlign();
void checkCastAlign() {
Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange);
}
Aquí está el método con un poco más de contexto:
/// 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;
}