Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C11 alignas vs. clang -Wcast-align

Tags:

So I have the following minimized C11 code that defines a struct containing a uint16_t (which means the struct it should be aligned to 2 bytes) and I want to cast a char buffer to a pointer to that struct.

With warnings turned all up, clang rightly complained that the alignment requirements of the struct are not met. So I added a C11 alignas specifier to the buffer to make sure the buffer is sufficiently aligned, but that didn't shut up clang.

My question is: am I doing something wrong with alignas? Or is it just that the -Wcast-align diagnostic is only looking at the type of the arguments and not also at the manually specified alignment? (I realize I can just cast to void* to silence the diagnostic, but since this piece of code is supposed to be portable, I don't want to side-step the diagnostic unless I am certain it is a false positive.)

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

Compiler options and error message:

$ 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 

Update: There is no warning when I move the buffer and its alignment into a struct. I interpret this as a hint that clang does indeed only look at the types for this warning.

#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; } 
like image 703
handschuhfach Avatar asked Feb 14 '15 14:02

handschuhfach


People also ask

What does alignas do?

The alignas type specifier is a portable, C++ standard way to specify custom alignment of variables and user defined types.

What is Alignof C++?

In C++11 the alignof operator used to returns the alignment, in bytes of the specified type. Syntax: alignof(type) Syntax Explanation: alignof: operator returns the alignment in byte, required for instances of type, which type is either complete type, array type or a reference type.

What is structure padding in C++?

Structure padding is a concept in C that adds the one or more empty bytes between the memory addresses to align the data in memory.


1 Answers

From clang source, in SemaChecking.cpp:~7862, it seems they are only looking at types like you mention:

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

It looks to me like clang is preparing for a c-style cast which in turn will check for cast alignment.

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

Here is the method with a little more context:

/// 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; } 
like image 186
bentank Avatar answered Sep 29 '22 04:09

bentank