C11 alignas vs. clang-Wcast-align

donc j'ai le code C11 minimisé suivant qui définit une structure contenant uint16_t (ce qui signifie que la structure doit être alignée à 2 octets) et je veux lancer un tampon de char vers un pointeur vers cette structure.

<!-Clang s'est plaint à juste titre que les exigences d'alignement de la structure ne sont pas respectées. J'ai donc ajouté un C11 alignas spécifiez au buffer pour s'assurer que le buffer est suffisamment aligné, mais cela n'a pas arrêté clang.

Mon la question Est: est-ce que je fais quelque chose de mal avec <!--4? Ou est-ce juste que le diagnostic-wcast-align ne regarde que le type des arguments et pas aussi l'alignement spécifié manuellement? (Je me rends compte que je peux juste lancer void* pour réduire au silence le diagnostic, mais puisque ce morceau de code est censé être portable, Je ne veux pas esquiver le diagnostic à moins d'être certain qu'il s'agit d'un faux positif.)

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

Options du compilateur et erreur 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;
                      ^~~~~~~~~~~~~~~~~~~~~~~~~

compilateur 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

mise à Jour: Il n'y a aucun avertissement quand je déplace le buffer et son alignement dans une structure. J'interprète ceci comme une indication que clang ne regarde en effet que les types pour cet avertissement.

#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;
}
21
demandé sur handschuhfach 2015-02-14 17:07:49

1 réponses

Du bruit à la source, dans SemaChecking.cpp:~7862, il semble qu'ils ne regardent que les types comme vous mentionnez:

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

  // else warn...

il me semble que clang se prépare pour un casting de style c qui, à son tour, vérifiera l'alignement du casting.

void CastOperation::CheckCStyleCast()
    -> Kind = CastKind Sema::PrepareScalarCast(...);
        -> if (Kind == CK_BitCast)
               checkCastAlign();

void checkCastAlign() {
  Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange);
}

Voici la méthode avec un peu plus de contexte:

/// 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;
}
2
répondu bentank 2015-05-02 06:56:06