[clang] f75b735 - [Clang][Attribute] Improve the AST/diagnoses fidelity of alignas and _Alignas
via cfe-commits
cfe-commits at lists.llvm.org
Thu May 25 16:42:20 PDT 2023
Author: yronglin
Date: 2023-05-26T07:41:26+08:00
New Revision: f75b73549d4adb7e111444f4144af7bffb532f91
URL: https://github.com/llvm/llvm-project/commit/f75b73549d4adb7e111444f4144af7bffb532f91
DIFF: https://github.com/llvm/llvm-project/commit/f75b73549d4adb7e111444f4144af7bffb532f91.diff
LOG: [Clang][Attribute] Improve the AST/diagnoses fidelity of alignas and _Alignas
- Fix diagnoses when the argument to `alignas` or `_Alignas` is an incomplete type.
Before:
```
./alignas.cpp:1:15: error: invalid application of 'alignof' to an incomplete type 'void'
class alignas(void) Foo {};
~^~~~~
1 error generated.
```
Now:
```
./alignas.cpp:1:15: error: invalid application of 'alignas' to an incomplete type 'void'
class alignas(void) Foo {};
~^~~~~
1 error generated.
```
- Improve the AST fidelity of `alignas` and `_Alignas` attribute.
Before:
```
AlignedAttr 0x13f07f278 <col:7> alignas
`-ConstantExpr 0x13f07f258 <col:15, col:21> 'unsigned long'
|-value: Int 8
`-UnaryExprOrTypeTraitExpr 0x13f07f118 <col:15, col:21> 'unsigned long' alignof 'void *'
```
Now:
```
AlignedAttr 0x14288c608 <col:7> alignas 'void *'
```
Reviewed By: erichkeane
Differential Revision: https://reviews.llvm.org/D150528
Added:
clang/test/Sema/aix-attr-aligned-vector-warn.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Parse/Parser.h
clang/include/clang/Sema/ParsedAttr.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/AttrImpl.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/Parse/ParseDecl.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/test/AST/ast-dump-attr.cpp
clang/test/Sema/aix-attr-aligned-vector-warn.c
clang/test/Sema/sizeless-1.c
clang/test/SemaCXX/attr-cxx0x.cpp
clang/test/SemaCXX/builtin-align-cxx.cpp
clang/test/SemaCXX/cxx11-attr-print.cpp
clang/test/SemaCXX/sizeless-1.cpp
clang/utils/TableGen/ClangAttrEmitter.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7a936051fe051..f1cc205c4a430 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -256,6 +256,9 @@ Attribute Changes in Clang
the compilation of the foreign language sources (e.g. Swift).
- The ``__has_attribute``, ``__has_c_attribute`` and ``__has_cpp_attribute``
preprocessor operators now return 1 also for attributes defined by plugins.
+- Improve the AST fidelity of ``alignas`` and ``_Alignas`` attribute. Before, we
+ model ``alignas(type-id)`` as though the user wrote ``alignas(alignof(type-id))``,
+ now we directly use ``alignas(type-id)``.
Improvements to Clang's diagnostics
-----------------------------------
@@ -307,6 +310,10 @@ Improvements to Clang's diagnostics
(`#62850: <https://github.com/llvm/llvm-project/issues/62850>`_).
- Clang now warns when any predefined macro is undefined or redefined, instead
of only some of them.
+- Clang now correctly diagnoses when the argument to ``alignas`` or ``_Alignas``
+ is an incomplete type.
+ (`#55175: <https://github.com/llvm/llvm-project/issues/55175>`_, and fixes an
+ incorrect mention of ``alignof`` in a diagnostic about ``alignas``).
Bug Fixes in This Version
-------------------------
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 1f4ccd0a3f3a0..43ea50a71f744 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3004,8 +3004,9 @@ class Parser : public CodeCompletionHandler {
SourceLocation EndLoc);
void ParseAtomicSpecifier(DeclSpec &DS);
- ExprResult ParseAlignArgument(SourceLocation Start,
- SourceLocation &EllipsisLoc);
+ ExprResult ParseAlignArgument(StringRef KWName, SourceLocation Start,
+ SourceLocation &EllipsisLoc, bool &IsType,
+ ParsedType &Ty);
void ParseAlignmentSpecifier(ParsedAttributes &Attrs,
SourceLocation *endLoc = nullptr);
ExprResult ParseExtIntegerArgument();
diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h
index 3612e9244f328..592580bccd234 100644
--- a/clang/include/clang/Sema/ParsedAttr.h
+++ b/clang/include/clang/Sema/ParsedAttr.h
@@ -273,12 +273,13 @@ class ParsedAttr final
/// Constructor for attributes with a single type argument.
ParsedAttr(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc,
- ParsedType typeArg, Form formUsed)
+ ParsedType typeArg, Form formUsed, SourceLocation ellipsisLoc)
: AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc, formUsed),
- NumArgs(0), Invalid(false), UsedAsTypeAttr(false),
- IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
- HasParsedType(true), HasProcessingCache(false),
- IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
+ EllipsisLoc(ellipsisLoc), NumArgs(0), Invalid(false),
+ UsedAsTypeAttr(false), IsAvailability(false),
+ IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true),
+ HasProcessingCache(false), IsPragmaClangAttribute(false),
+ Info(ParsedAttrInfo::get(*this)) {
new (&getTypeBuffer()) ParsedType(typeArg);
}
@@ -782,13 +783,14 @@ class AttributePool {
SourceRange attrRange,
IdentifierInfo *scopeName,
SourceLocation scopeLoc, ParsedType typeArg,
- ParsedAttr::Form formUsed) {
+ ParsedAttr::Form formUsed,
+ SourceLocation ellipsisLoc) {
void *memory = allocate(
ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
detail::TypeTagForDatatypeData, ParsedType,
detail::PropertyData>(0, 0, 0, 1, 0));
return add(new (memory) ParsedAttr(attrName, attrRange, scopeName, scopeLoc,
- typeArg, formUsed));
+ typeArg, formUsed, ellipsisLoc));
}
ParsedAttr *
@@ -1001,9 +1003,11 @@ class ParsedAttributes : public ParsedAttributesView {
/// Add an attribute with a single type argument.
ParsedAttr *addNewTypeAttr(IdentifierInfo *attrName, SourceRange attrRange,
IdentifierInfo *scopeName, SourceLocation scopeLoc,
- ParsedType typeArg, ParsedAttr::Form formUsed) {
- ParsedAttr *attr = pool.createTypeAttribute(attrName, attrRange, scopeName,
- scopeLoc, typeArg, formUsed);
+ ParsedType typeArg, ParsedAttr::Form formUsed,
+ SourceLocation ellipsisLoc = SourceLocation()) {
+ ParsedAttr *attr =
+ pool.createTypeAttribute(attrName, attrRange, scopeName, scopeLoc,
+ typeArg, formUsed, ellipsisLoc);
addAtEnd(attr);
return attr;
}
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index cb38329ca73a5..6f5fa86dc9ccc 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5720,6 +5720,11 @@ class Sema final {
bool CheckTypeTraitArity(unsigned Arity, SourceLocation Loc, size_t N);
+ bool ActOnAlignasTypeArgument(StringRef KWName, ParsedType Ty,
+ SourceLocation OpLoc, SourceRange R);
+ bool CheckAlignasTypeArgument(StringRef KWName, TypeSourceInfo *TInfo,
+ SourceLocation OpLoc, SourceRange R);
+
ExprResult CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
SourceLocation OpLoc,
UnaryExprOrTypeTrait ExprKind,
@@ -5738,7 +5743,8 @@ class Sema final {
bool CheckUnaryExprOrTypeTraitOperand(Expr *E, UnaryExprOrTypeTrait ExprKind);
bool CheckUnaryExprOrTypeTraitOperand(QualType ExprType, SourceLocation OpLoc,
SourceRange ExprRange,
- UnaryExprOrTypeTrait ExprKind);
+ UnaryExprOrTypeTrait ExprKind,
+ StringRef KWName);
ExprResult ActOnSizeofParameterPackExpr(Scope *S,
SourceLocation OpLoc,
IdentifierInfo &Name,
diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp
index 0adcca7731d91..f41ab77ef164c 100644
--- a/clang/lib/AST/AttrImpl.cpp
+++ b/clang/lib/AST/AttrImpl.cpp
@@ -239,4 +239,33 @@ void OMPDeclareVariantAttr::printPrettyPragma(
}
}
+unsigned AlignedAttr::getAlignment(ASTContext &Ctx) const {
+ assert(!isAlignmentDependent());
+ if (getCachedAlignmentValue())
+ return *getCachedAlignmentValue();
+
+ // Handle alignmentType case.
+ if (!isAlignmentExpr()) {
+ QualType T = getAlignmentType()->getType();
+
+ // C++ [expr.alignof]p3:
+ // When alignof is applied to a reference type, the result is the
+ // alignment of the referenced type.
+ T = T.getNonReferenceType();
+
+ if (T.getQualifiers().hasUnaligned())
+ return Ctx.getCharWidth();
+
+ return Ctx.getTypeAlignInChars(T.getTypePtr()).getQuantity() *
+ Ctx.getCharWidth();
+ }
+
+ // Handle alignmentExpr case.
+ if (alignmentExpr)
+ return alignmentExpr->EvaluateKnownConstInt(Ctx).getZExtValue() *
+ Ctx.getCharWidth();
+
+ return Ctx.getTargetDefaultAlignForAttributeAligned();
+}
+
#include "clang/AST/AttrImpl.inc"
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 1f1ce2a18bd4a..1df992cc4ce2f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9025,8 +9025,7 @@ static CharUnits GetAlignOfType(EvalInfo &Info, QualType T,
// C++ [expr.alignof]p3:
// When alignof is applied to a reference type, the result is the
// alignment of the referenced type.
- if (const ReferenceType *Ref = T->getAs<ReferenceType>())
- T = Ref->getPointeeType();
+ T = T.getNonReferenceType();
if (T.getQualifiers().hasUnaligned())
return CharUnits::One();
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index b35912f1da683..0e62f0dff980f 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -2984,24 +2984,26 @@ Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) {
/// ParseAlignArgument - Parse the argument to an alignment-specifier.
///
-/// FIXME: Simply returns an alignof() expression if the argument is a
-/// type. Ideally, the type should be propagated directly into Sema.
-///
/// [C11] type-id
/// [C11] constant-expression
/// [C++0x] type-id ...[opt]
/// [C++0x] assignment-expression ...[opt]
-ExprResult Parser::ParseAlignArgument(SourceLocation Start,
- SourceLocation &EllipsisLoc) {
+ExprResult Parser::ParseAlignArgument(StringRef KWName, SourceLocation Start,
+ SourceLocation &EllipsisLoc, bool &IsType,
+ ParsedType &TypeResult) {
ExprResult ER;
if (isTypeIdInParens()) {
SourceLocation TypeLoc = Tok.getLocation();
ParsedType Ty = ParseTypeName().get();
SourceRange TypeRange(Start, Tok.getLocation());
- ER = Actions.ActOnUnaryExprOrTypeTraitExpr(TypeLoc, UETT_AlignOf, true,
- Ty.getAsOpaquePtr(), TypeRange);
- } else
+ if (Actions.ActOnAlignasTypeArgument(KWName, Ty, TypeLoc, TypeRange))
+ return ExprError();
+ TypeResult = Ty;
+ IsType = true;
+ } else {
ER = ParseConstantExpression();
+ IsType = false;
+ }
if (getLangOpts().CPlusPlus11)
TryConsumeToken(tok::ellipsis, EllipsisLoc);
@@ -3021,17 +3023,21 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
SourceLocation *EndLoc) {
assert(Tok.isOneOf(tok::kw_alignas, tok::kw__Alignas) &&
"Not an alignment-specifier!");
-
- IdentifierInfo *KWName = Tok.getIdentifierInfo();
- auto Kind = Tok.getKind();
+ Token KWTok = Tok;
+ IdentifierInfo *KWName = KWTok.getIdentifierInfo();
+ auto Kind = KWTok.getKind();
SourceLocation KWLoc = ConsumeToken();
BalancedDelimiterTracker T(*this, tok::l_paren);
if (T.expectAndConsume())
return;
+ bool IsType;
+ ParsedType TypeResult;
SourceLocation EllipsisLoc;
- ExprResult ArgExpr = ParseAlignArgument(T.getOpenLocation(), EllipsisLoc);
+ ExprResult ArgExpr =
+ ParseAlignArgument(PP.getSpelling(KWTok), T.getOpenLocation(),
+ EllipsisLoc, IsType, TypeResult);
if (ArgExpr.isInvalid()) {
T.skipToEnd();
return;
@@ -3041,10 +3047,15 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
if (EndLoc)
*EndLoc = T.getCloseLocation();
- ArgsVector ArgExprs;
- ArgExprs.push_back(ArgExpr.get());
- Attrs.addNew(KWName, KWLoc, nullptr, KWLoc, ArgExprs.data(), 1, Kind,
- EllipsisLoc);
+ if (IsType) {
+ Attrs.addNewTypeAttr(KWName, KWLoc, nullptr, KWLoc, TypeResult, Kind,
+ EllipsisLoc);
+ } else {
+ ArgsVector ArgExprs;
+ ArgExprs.push_back(ArgExpr.get());
+ Attrs.addNew(KWName, KWLoc, nullptr, KWLoc, ArgExprs.data(), 1, Kind,
+ EllipsisLoc);
+ }
}
ExprResult Parser::ParseExtIntegerArgument() {
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 3cb3250f67318..562fba190f1a4 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -4333,6 +4333,27 @@ void Sema::AddAlignValueAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E) {
}
static void handleAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ if (AL.hasParsedType()) {
+ const ParsedType &TypeArg = AL.getTypeArg();
+ TypeSourceInfo *TInfo;
+ (void)S.GetTypeFromParser(
+ ParsedType::getFromOpaquePtr(TypeArg.getAsOpaquePtr()), &TInfo);
+ if (AL.isPackExpansion() &&
+ !TInfo->getType()->containsUnexpandedParameterPack()) {
+ S.Diag(AL.getEllipsisLoc(),
+ diag::err_pack_expansion_without_parameter_packs);
+ return;
+ }
+
+ if (!AL.isPackExpansion() &&
+ S.DiagnoseUnexpandedParameterPack(TInfo->getTypeLoc().getBeginLoc(),
+ TInfo, Sema::UPPC_Expression))
+ return;
+
+ S.AddAlignedAttr(D, AL, TInfo, AL.isPackExpansion());
+ return;
+ }
+
// check the attribute arguments.
if (AL.getNumArgs() > 1) {
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
@@ -4357,53 +4378,61 @@ static void handleAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
S.AddAlignedAttr(D, AL, E, AL.isPackExpansion());
}
+/// Perform checking of type validity
+///
+/// C++11 [dcl.align]p1:
+/// An alignment-specifier may be applied to a variable or to a class
+/// data member, but it shall not be applied to a bit-field, a function
+/// parameter, the formal parameter of a catch clause, or a variable
+/// declared with the register storage class specifier. An
+/// alignment-specifier may also be applied to the declaration of a class
+/// or enumeration type.
+/// CWG 2354:
+/// CWG agreed to remove permission for alignas to be applied to
+/// enumerations.
+/// C11 6.7.5/2:
+/// An alignment attribute shall not be specified in a declaration of
+/// a typedef, or a bit-field, or a function, or a parameter, or an
+/// object declared with the register storage-class specifier.
+static bool validateAlignasAppliedType(Sema &S, Decl *D,
+ const AlignedAttr &Attr,
+ SourceLocation AttrLoc) {
+ int DiagKind = -1;
+ if (isa<ParmVarDecl>(D)) {
+ DiagKind = 0;
+ } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ if (VD->getStorageClass() == SC_Register)
+ DiagKind = 1;
+ if (VD->isExceptionVariable())
+ DiagKind = 2;
+ } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
+ if (FD->isBitField())
+ DiagKind = 3;
+ } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
+ if (ED->getLangOpts().CPlusPlus)
+ DiagKind = 4;
+ } else if (!isa<TagDecl>(D)) {
+ return S.Diag(AttrLoc, diag::err_attribute_wrong_decl_type)
+ << &Attr
+ << (Attr.isC11() ? ExpectedVariableOrField
+ : ExpectedVariableFieldOrTag);
+ }
+ if (DiagKind != -1) {
+ return S.Diag(AttrLoc, diag::err_alignas_attribute_wrong_decl_type)
+ << &Attr << DiagKind;
+ }
+ return false;
+}
+
void Sema::AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
bool IsPackExpansion) {
AlignedAttr TmpAttr(Context, CI, true, E);
SourceLocation AttrLoc = CI.getLoc();
// C++11 alignas(...) and C11 _Alignas(...) have additional requirements.
- if (TmpAttr.isAlignas()) {
- // C++11 [dcl.align]p1:
- // An alignment-specifier may be applied to a variable or to a class
- // data member, but it shall not be applied to a bit-field, a function
- // parameter, the formal parameter of a catch clause, or a variable
- // declared with the register storage class specifier. An
- // alignment-specifier may also be applied to the declaration of a class
- // or enumeration type.
- // CWG 2354:
- // CWG agreed to remove permission for alignas to be applied to
- // enumerations.
- // C11 6.7.5/2:
- // An alignment attribute shall not be specified in a declaration of
- // a typedef, or a bit-field, or a function, or a parameter, or an
- // object declared with the register storage-class specifier.
- int DiagKind = -1;
- if (isa<ParmVarDecl>(D)) {
- DiagKind = 0;
- } else if (const auto *VD = dyn_cast<VarDecl>(D)) {
- if (VD->getStorageClass() == SC_Register)
- DiagKind = 1;
- if (VD->isExceptionVariable())
- DiagKind = 2;
- } else if (const auto *FD = dyn_cast<FieldDecl>(D)) {
- if (FD->isBitField())
- DiagKind = 3;
- } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
- if (ED->getLangOpts().CPlusPlus)
- DiagKind = 4;
- } else if (!isa<TagDecl>(D)) {
- Diag(AttrLoc, diag::err_attribute_wrong_decl_type) << &TmpAttr
- << (TmpAttr.isC11() ? ExpectedVariableOrField
- : ExpectedVariableFieldOrTag);
- return;
- }
- if (DiagKind != -1) {
- Diag(AttrLoc, diag::err_alignas_attribute_wrong_decl_type)
- << &TmpAttr << DiagKind;
- return;
- }
- }
+ if (TmpAttr.isAlignas() &&
+ validateAlignasAppliedType(*this, D, TmpAttr, AttrLoc))
+ return;
if (E->isValueDependent()) {
// We can't support a dependent alignment on a non-dependent type,
@@ -4480,15 +4509,56 @@ void Sema::AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
AlignedAttr *AA = ::new (Context) AlignedAttr(Context, CI, true, ICE.get());
AA->setPackExpansion(IsPackExpansion);
+ AA->setCachedAlignmentValue(
+ static_cast<unsigned>(AlignVal * Context.getCharWidth()));
D->addAttr(AA);
}
void Sema::AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI,
TypeSourceInfo *TS, bool IsPackExpansion) {
- // FIXME: Cache the number on the AL object if non-dependent?
- // FIXME: Perform checking of type validity
+ AlignedAttr TmpAttr(Context, CI, false, TS);
+ SourceLocation AttrLoc = CI.getLoc();
+
+ // C++11 alignas(...) and C11 _Alignas(...) have additional requirements.
+ if (TmpAttr.isAlignas() &&
+ validateAlignasAppliedType(*this, D, TmpAttr, AttrLoc))
+ return;
+
+ if (TS->getType()->isDependentType()) {
+ // We can't support a dependent alignment on a non-dependent type,
+ // because we have no way to model that a type is "type-dependent"
+ // but not dependent in any other way.
+ if (const auto *TND = dyn_cast<TypedefNameDecl>(D)) {
+ if (!TND->getUnderlyingType()->isDependentType()) {
+ Diag(AttrLoc, diag::err_alignment_dependent_typedef_name)
+ << TS->getTypeLoc().getSourceRange();
+ return;
+ }
+ }
+
+ AlignedAttr *AA = ::new (Context) AlignedAttr(Context, CI, false, TS);
+ AA->setPackExpansion(IsPackExpansion);
+ D->addAttr(AA);
+ return;
+ }
+
+ const auto *VD = dyn_cast<VarDecl>(D);
+ unsigned AlignVal = TmpAttr.getAlignment(Context);
+ // On AIX, an aligned attribute can not decrease the alignment when applied
+ // to a variable declaration with vector type.
+ if (VD && Context.getTargetInfo().getTriple().isOSAIX()) {
+ const Type *Ty = VD->getType().getTypePtr();
+ if (Ty->isVectorType() &&
+ Context.toCharUnitsFromBits(AlignVal).getQuantity() < 16) {
+ Diag(VD->getLocation(), diag::warn_aligned_attr_underaligned)
+ << VD->getType() << 16;
+ return;
+ }
+ }
+
AlignedAttr *AA = ::new (Context) AlignedAttr(Context, CI, false, TS);
AA->setPackExpansion(IsPackExpansion);
+ AA->setCachedAlignmentValue(AlignVal);
D->addAttr(AA);
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index fb55c95bd0689..72f1a2b82b46e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4355,70 +4355,6 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
return false;
}
-/// Check the constraints on operands to unary expression and type
-/// traits.
-///
-/// This will complete any types necessary, and validate the various constraints
-/// on those operands.
-///
-/// The UsualUnaryConversions() function is *not* called by this routine.
-/// C99 6.3.2.1p[2-4] all state:
-/// Except when it is the operand of the sizeof operator ...
-///
-/// C++ [expr.sizeof]p4
-/// The lvalue-to-rvalue, array-to-pointer, and function-to-pointer
-/// standard conversions are not applied to the operand of sizeof.
-///
-/// This policy is followed for all of the unary trait expressions.
-bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
- SourceLocation OpLoc,
- SourceRange ExprRange,
- UnaryExprOrTypeTrait ExprKind) {
- if (ExprType->isDependentType())
- return false;
-
- // C++ [expr.sizeof]p2:
- // When applied to a reference or a reference type, the result
- // is the size of the referenced type.
- // C++11 [expr.alignof]p3:
- // When alignof is applied to a reference type, the result
- // shall be the alignment of the referenced type.
- if (const ReferenceType *Ref = ExprType->getAs<ReferenceType>())
- ExprType = Ref->getPointeeType();
-
- // C11 6.5.3.4/3, C++11 [expr.alignof]p3:
- // When alignof or _Alignof is applied to an array type, the result
- // is the alignment of the element type.
- if (ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf ||
- ExprKind == UETT_OpenMPRequiredSimdAlign)
- ExprType = Context.getBaseElementType(ExprType);
-
- if (ExprKind == UETT_VecStep)
- return CheckVecStepTraitOperandType(*this, ExprType, OpLoc, ExprRange);
-
- // Explicitly list some types as extensions.
- if (!CheckExtensionTraitOperandType(*this, ExprType, OpLoc, ExprRange,
- ExprKind))
- return false;
-
- if (RequireCompleteSizedType(
- OpLoc, ExprType, diag::err_sizeof_alignof_incomplete_or_sizeless_type,
- getTraitSpelling(ExprKind), ExprRange))
- return true;
-
- if (ExprType->isFunctionType()) {
- Diag(OpLoc, diag::err_sizeof_alignof_function_type)
- << getTraitSpelling(ExprKind) << ExprRange;
- return true;
- }
-
- if (CheckObjCTraitOperandConstraints(*this, ExprType, OpLoc, ExprRange,
- ExprKind))
- return true;
-
- return false;
-}
-
static bool CheckAlignOfExpr(Sema &S, Expr *E, UnaryExprOrTypeTrait ExprKind) {
// Cannot know anything else if the expression is dependent.
if (E->isTypeDependent())
@@ -4596,23 +4532,69 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
} while (!T.isNull() && T->isVariablyModifiedType());
}
-/// Build a sizeof or alignof expression given a type operand.
-ExprResult
-Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
- SourceLocation OpLoc,
- UnaryExprOrTypeTrait ExprKind,
- SourceRange R) {
- if (!TInfo)
- return ExprError();
+/// Check the constraints on operands to unary expression and type
+/// traits.
+///
+/// This will complete any types necessary, and validate the various constraints
+/// on those operands.
+///
+/// The UsualUnaryConversions() function is *not* called by this routine.
+/// C99 6.3.2.1p[2-4] all state:
+/// Except when it is the operand of the sizeof operator ...
+///
+/// C++ [expr.sizeof]p4
+/// The lvalue-to-rvalue, array-to-pointer, and function-to-pointer
+/// standard conversions are not applied to the operand of sizeof.
+///
+/// This policy is followed for all of the unary trait expressions.
+bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
+ SourceLocation OpLoc,
+ SourceRange ExprRange,
+ UnaryExprOrTypeTrait ExprKind,
+ StringRef KWName) {
+ if (ExprType->isDependentType())
+ return false;
- QualType T = TInfo->getType();
+ // C++ [expr.sizeof]p2:
+ // When applied to a reference or a reference type, the result
+ // is the size of the referenced type.
+ // C++11 [expr.alignof]p3:
+ // When alignof is applied to a reference type, the result
+ // shall be the alignment of the referenced type.
+ if (const ReferenceType *Ref = ExprType->getAs<ReferenceType>())
+ ExprType = Ref->getPointeeType();
- if (!T->isDependentType() &&
- CheckUnaryExprOrTypeTraitOperand(T, OpLoc, R, ExprKind))
- return ExprError();
+ // C11 6.5.3.4/3, C++11 [expr.alignof]p3:
+ // When alignof or _Alignof is applied to an array type, the result
+ // is the alignment of the element type.
+ if (ExprKind == UETT_AlignOf || ExprKind == UETT_PreferredAlignOf ||
+ ExprKind == UETT_OpenMPRequiredSimdAlign)
+ ExprType = Context.getBaseElementType(ExprType);
+
+ if (ExprKind == UETT_VecStep)
+ return CheckVecStepTraitOperandType(*this, ExprType, OpLoc, ExprRange);
+
+ // Explicitly list some types as extensions.
+ if (!CheckExtensionTraitOperandType(*this, ExprType, OpLoc, ExprRange,
+ ExprKind))
+ return false;
+
+ if (RequireCompleteSizedType(
+ OpLoc, ExprType, diag::err_sizeof_alignof_incomplete_or_sizeless_type,
+ KWName, ExprRange))
+ return true;
+
+ if (ExprType->isFunctionType()) {
+ Diag(OpLoc, diag::err_sizeof_alignof_function_type) << KWName << ExprRange;
+ return true;
+ }
+
+ if (CheckObjCTraitOperandConstraints(*this, ExprType, OpLoc, ExprRange,
+ ExprKind))
+ return true;
- if (T->isVariablyModifiedType() && FunctionScopes.size() > 1) {
- if (auto *TT = T->getAs<TypedefType>()) {
+ if (ExprType->isVariablyModifiedType() && FunctionScopes.size() > 1) {
+ if (auto *TT = ExprType->getAs<TypedefType>()) {
for (auto I = FunctionScopes.rbegin(),
E = std::prev(FunctionScopes.rend());
I != E; ++I) {
@@ -4629,17 +4611,37 @@ Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
if (DC) {
if (DC->containsDecl(TT->getDecl()))
break;
- captureVariablyModifiedType(Context, T, CSI);
+ captureVariablyModifiedType(Context, ExprType, CSI);
}
}
}
}
- // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t.
+ return false;
+}
+
+/// Build a sizeof or alignof expression given a type operand.
+ExprResult Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
+ SourceLocation OpLoc,
+ UnaryExprOrTypeTrait ExprKind,
+ SourceRange R) {
+ if (!TInfo)
+ return ExprError();
+
+ QualType T = TInfo->getType();
+
+ if (!T->isDependentType() &&
+ CheckUnaryExprOrTypeTraitOperand(T, OpLoc, R, ExprKind,
+ getTraitSpelling(ExprKind)))
+ return ExprError();
+
+ // Adds overload of TransformToPotentiallyEvaluated for TypeSourceInfo to
+ // properly deal with VLAs in nested calls of sizeof and typeof.
if (isUnevaluatedContext() && ExprKind == UETT_SizeOf &&
TInfo->getType()->isVariablyModifiedType())
TInfo = TransformToPotentiallyEvaluated(TInfo);
+ // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t.
return new (Context) UnaryExprOrTypeTraitExpr(
ExprKind, TInfo, Context.getSizeType(), OpLoc, R.getEnd());
}
@@ -4708,6 +4710,29 @@ Sema::ActOnUnaryExprOrTypeTraitExpr(SourceLocation OpLoc,
return Result;
}
+bool Sema::CheckAlignasTypeArgument(StringRef KWName, TypeSourceInfo *TInfo,
+ SourceLocation OpLoc, SourceRange R) {
+ if (!TInfo)
+ return true;
+ return CheckUnaryExprOrTypeTraitOperand(TInfo->getType(), OpLoc, R,
+ UETT_AlignOf, KWName);
+}
+
+/// ActOnAlignasTypeArgument - Handle @c alignas(type-id) and @c
+/// _Alignas(type-name) .
+/// [dcl.align] An alignment-specifier of the form
+/// alignas(type-id) has the same effect as alignas(alignof(type-id)).
+///
+/// [N1570 6.7.5] _Alignas(type-name) is equivalent to
+/// _Alignas(_Alignof(type-name)).
+bool Sema::ActOnAlignasTypeArgument(StringRef KWName, ParsedType Ty,
+ SourceLocation OpLoc, SourceRange R) {
+ TypeSourceInfo *TInfo;
+ (void)GetTypeFromParser(ParsedType::getFromOpaquePtr(Ty.getAsOpaquePtr()),
+ &TInfo);
+ return CheckAlignasTypeArgument(KWName, TInfo, OpLoc, R);
+}
+
static QualType CheckRealImagOperand(Sema &S, ExprResult &V, SourceLocation Loc,
bool IsReal) {
if (V.get()->isTypeDependent())
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 480cefd1efcd1..b7879e5a7f854 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -95,11 +95,14 @@ static void instantiateDependentAlignedAttr(
if (!Result.isInvalid())
S.AddAlignedAttr(New, *Aligned, Result.getAs<Expr>(), IsPackExpansion);
} else {
- TypeSourceInfo *Result = S.SubstType(Aligned->getAlignmentType(),
- TemplateArgs, Aligned->getLocation(),
- DeclarationName());
- if (Result)
- S.AddAlignedAttr(New, *Aligned, Result, IsPackExpansion);
+ if (TypeSourceInfo *Result =
+ S.SubstType(Aligned->getAlignmentType(), TemplateArgs,
+ Aligned->getLocation(), DeclarationName())) {
+ if (!S.CheckAlignasTypeArgument(Aligned->getSpelling(), Result,
+ Aligned->getLocation(),
+ Result->getTypeLoc().getSourceRange()))
+ S.AddAlignedAttr(New, *Aligned, Result, IsPackExpansion);
+ }
}
}
diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp
index 15ef878f31573..8fd4a8f3a54c6 100644
--- a/clang/test/AST/ast-dump-attr.cpp
+++ b/clang/test/AST/ast-dump-attr.cpp
@@ -129,6 +129,38 @@ extern int x;
// CHECK-NEXT: AlignedAttr{{.*}} Inherited
}
+namespace TestAligns {
+
+template<typename...T> struct my_union {
+ alignas(T...) char buffer[1024];
+};
+
+template<typename...T> struct my_union2 {
+ _Alignas(T...) char buffer[1024];
+};
+
+struct alignas(8) A { char c; };
+struct alignas(4) B { short s; };
+struct C { char a[16]; };
+
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct my_union
+// CHECK: CXXRecordDecl {{.*}} implicit struct my_union
+// CHECK: FieldDecl {{.*}} buffer 'char[1024]'
+// CHECK-NEXT: AlignedAttr {{.*}} alignas 'TestAligns::A':'TestAligns::A'
+// CHECK-NEXT: AlignedAttr {{.*}} alignas 'TestAligns::B':'TestAligns::B'
+// CHECK-NEXT: AlignedAttr {{.*}} alignas 'TestAligns::C':'TestAligns::C'
+my_union<A, B, C> my_union_val;
+
+// CHECK: ClassTemplateSpecializationDecl {{.*}} struct my_union2
+// CHECK: CXXRecordDecl {{.*}} implicit struct my_union2
+// CHECK: FieldDecl {{.*}} buffer 'char[1024]'
+// CHECK-NEXT: AlignedAttr {{.*}} _Alignas 'TestAligns::A':'TestAligns::A'
+// CHECK-NEXT: AlignedAttr {{.*}} _Alignas 'TestAligns::B':'TestAligns::B'
+// CHECK-NEXT: AlignedAttr {{.*}} _Alignas 'TestAligns::C':'TestAligns::C'
+my_union2<A, B, C> my_union2_val;
+
+} // namespace TestAligns
+
int __attribute__((cdecl)) TestOne(void), TestTwo(void);
// CHECK: FunctionDecl{{.*}}TestOne{{.*}}__attribute__((cdecl))
// CHECK: FunctionDecl{{.*}}TestTwo{{.*}}__attribute__((cdecl))
diff --git a/clang/test/Sema/aix-attr-aligned-vector-warn.c b/clang/test/Sema/aix-attr-aligned-vector-warn.c
index af2e1a89268a2..41c0d39ded82d 100644
--- a/clang/test/Sema/aix-attr-aligned-vector-warn.c
+++ b/clang/test/Sema/aix-attr-aligned-vector-warn.c
@@ -6,6 +6,7 @@ int escape(vector int*);
typedef vector int __attribute__((aligned(8))) UnderAlignedVI;
UnderAlignedVI TypedefedGlobal;
+vector int V __attribute__((aligned(8))); // expected-warning {{requested alignment is less than minimum alignment of 16 for type '__vector int' (vector of 4 'int' values)}}
vector int V __attribute__((aligned(8))); // expected-warning {{requested alignment is less than minimum alignment of 16 for type '__vector int' (vector of 4 'int' values)}}
int localTypedefed(void) {
diff --git a/clang/test/Sema/aix-attr-aligned-vector-warn.cpp b/clang/test/Sema/aix-attr-aligned-vector-warn.cpp
new file mode 100644
index 0000000000000..c965a1d9dbc47
--- /dev/null
+++ b/clang/test/Sema/aix-attr-aligned-vector-warn.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple powerpc64-unknown-aix -target-feature +altivec -target-cpu pwr7 -verify -fsyntax-only %s
+// RUN: %clang_cc1 -triple powerpc-unknown-aix -target-feature +altivec -target-cpu pwr7 -verify -fsyntax-only %s
+
+struct alignas(8) Align8 {
+ void *a, *b;
+};
+
+alignas(8) vector int V1; // expected-warning {{requested alignment is less than minimum alignment of 16 for type '__vector int' (vector of 4 'int' values)}}
+alignas(Align8) vector int V2; // expected-warning {{requested alignment is less than minimum alignment of 16 for type '__vector int' (vector of 4 'int' values)}}
diff --git a/clang/test/Sema/sizeless-1.c b/clang/test/Sema/sizeless-1.c
index bbc36e32181f1..9ec884b9f1fda 100644
--- a/clang/test/Sema/sizeless-1.c
+++ b/clang/test/Sema/sizeless-1.c
@@ -64,7 +64,7 @@ void func(int sel) {
svint8_t __attribute__((aligned(4))) aligned_int8_2; // expected-error {{'aligned' attribute cannot be applied to sizeless type 'svint8_t'}}
svint8_t _Alignas(int) aligned_int8_3; // expected-error {{'_Alignas' attribute cannot be applied to sizeless type 'svint8_t'}}
- int _Alignas(svint8_t) aligned_int; // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}}
+ int _Alignas(svint8_t) aligned_int; // expected-error {{invalid application of '_Alignas' to sizeless type 'svint8_t'}}
// Using pointers to sizeless data isn't wrong here, but because the
// type is incomplete, it doesn't provide any alignment guarantees.
diff --git a/clang/test/SemaCXX/attr-cxx0x.cpp b/clang/test/SemaCXX/attr-cxx0x.cpp
index b405e11394818..4d64d2b0cd8c6 100644
--- a/clang/test/SemaCXX/attr-cxx0x.cpp
+++ b/clang/test/SemaCXX/attr-cxx0x.cpp
@@ -50,3 +50,6 @@ static_assert(alignof(int(int)) >= 1, "alignof(function) not positive"); // expe
void func(void);
alignas(4) auto PR19252 = 0;
+
+// Check the diagnostic message
+class alignas(void) AlignasVoid {}; // expected-error {{invalid application of 'alignas' to an incomplete type 'void'}}
diff --git a/clang/test/SemaCXX/builtin-align-cxx.cpp b/clang/test/SemaCXX/builtin-align-cxx.cpp
index 8a89b53c7103b..8ecd513f439c2 100644
--- a/clang/test/SemaCXX/builtin-align-cxx.cpp
+++ b/clang/test/SemaCXX/builtin-align-cxx.cpp
@@ -238,3 +238,6 @@ static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<uns
static_assert(!__builtin_is_aligned(static_cast<unsigned long>(7), static_cast<signed long>(4)), "");
static_assert(!__builtin_is_aligned(static_cast<signed long>(7), static_cast<unsigned short>(4)), "");
static_assert(!__builtin_is_aligned(static_cast<unsigned short>(7), static_cast<signed long>(4)), "");
+
+// Check the diagnostic message
+_Alignas(void) char align_void_array[1]; // expected-error {{invalid application of '_Alignas' to an incomplete type 'void'}}
diff --git a/clang/test/SemaCXX/cxx11-attr-print.cpp b/clang/test/SemaCXX/cxx11-attr-print.cpp
index e7c00b76d0383..2bf0ea5d82587 100644
--- a/clang/test/SemaCXX/cxx11-attr-print.cpp
+++ b/clang/test/SemaCXX/cxx11-attr-print.cpp
@@ -28,7 +28,7 @@ int e __attribute__((deprecated("warning", "fixit")));
// CHECK: int cxx11_alignas alignas(4);
alignas(4) int cxx11_alignas;
-// CHECK: int c11_alignas _Alignas(alignof(int));
+// CHECK: int c11_alignas _Alignas(int);
_Alignas(int) int c11_alignas;
// CHECK: void foo() __attribute__((const));
@@ -65,11 +65,13 @@ void f8 (void *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
// CHECK: int m __attribute__((aligned(4
// CHECK: int n alignas(4
+// CHECK: int p alignas(int
// CHECK: static int f() __attribute__((pure))
// CHECK: static int g() {{\[}}[gnu::pure]]
template <typename T> struct S {
__attribute__((aligned(4))) int m;
alignas(4) int n;
+ alignas(int) int p;
__attribute__((pure)) static int f() {
return 0;
}
diff --git a/clang/test/SemaCXX/sizeless-1.cpp b/clang/test/SemaCXX/sizeless-1.cpp
index ed0416f17995b..368a3eeb6955b 100644
--- a/clang/test/SemaCXX/sizeless-1.cpp
+++ b/clang/test/SemaCXX/sizeless-1.cpp
@@ -73,7 +73,7 @@ void func(int sel) {
svint8_t __attribute__((aligned(4))) aligned_int8_2; // expected-error {{'aligned' attribute cannot be applied to sizeless type 'svint8_t'}}
svint8_t _Alignas(int) aligned_int8_3; // expected-error {{'_Alignas' attribute cannot be applied to sizeless type 'svint8_t'}}
- int _Alignas(svint8_t) aligned_int; // expected-error {{invalid application of 'alignof' to sizeless type 'svint8_t'}}
+ int _Alignas(svint8_t) aligned_int; // expected-error {{invalid application of '_Alignas' to sizeless type 'svint8_t'}}
// Using pointers to sizeless data isn't wrong here, but because the
// type is incomplete, it doesn't provide any alignment guarantees.
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index b335fbfb2e83f..b0afcb03ec269 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -508,6 +508,16 @@ namespace {
OS << " assert(!is" << getLowerName() << "Expr);\n";
OS << " return " << getLowerName() << "Type;\n";
OS << " }";
+
+ OS << " std::optional<unsigned> getCached" << getUpperName()
+ << "Value() const {\n";
+ OS << " return " << getLowerName() << "Cache;\n";
+ OS << " }";
+
+ OS << " void setCached" << getUpperName()
+ << "Value(unsigned AlignVal) {\n";
+ OS << " " << getLowerName() << "Cache = AlignVal;\n";
+ OS << " }";
}
void writeAccessorDefinitions(raw_ostream &OS) const override {
@@ -530,21 +540,6 @@ namespace {
OS << " return " << getLowerName()
<< "Type->getType()->containsErrors();\n";
OS << "}\n";
-
- // FIXME: Do not do the calculation here
- // FIXME: Handle types correctly
- // A null pointer means maximum alignment
- OS << "unsigned " << getAttrName() << "Attr::get" << getUpperName()
- << "(ASTContext &Ctx) const {\n";
- OS << " assert(!is" << getUpperName() << "Dependent());\n";
- OS << " if (is" << getLowerName() << "Expr)\n";
- OS << " return " << getLowerName() << "Expr ? " << getLowerName()
- << "Expr->EvaluateKnownConstInt(Ctx).getZExtValue()"
- << " * Ctx.getCharWidth() : "
- << "Ctx.getTargetDefaultAlignForAttributeAligned();\n";
- OS << " else\n";
- OS << " return 0; // FIXME\n";
- OS << "}\n";
}
void writeASTVisitorTraversal(raw_ostream &OS) const override {
@@ -601,7 +596,8 @@ namespace {
OS << "union {\n";
OS << "Expr *" << getLowerName() << "Expr;\n";
OS << "TypeSourceInfo *" << getLowerName() << "Type;\n";
- OS << "};";
+ OS << "};\n";
+ OS << "std::optional<unsigned> " << getLowerName() << "Cache;\n";
}
void writePCHReadArgs(raw_ostream &OS) const override {
@@ -628,14 +624,21 @@ namespace {
}
std::string getIsOmitted() const override {
- return "!is" + getLowerName().str() + "Expr || !" + getLowerName().str()
- + "Expr";
+ return "!((is" + getLowerName().str() + "Expr && " +
+ getLowerName().str() + "Expr) || (!is" + getLowerName().str() +
+ "Expr && " + getLowerName().str() + "Type))";
}
void writeValue(raw_ostream &OS) const override {
OS << "\";\n";
- OS << " " << getLowerName()
+ OS << " if (is" << getLowerName() << "Expr && " << getLowerName()
+ << "Expr)";
+ OS << " " << getLowerName()
<< "Expr->printPretty(OS, nullptr, Policy);\n";
+ OS << " if (!is" << getLowerName() << "Expr && " << getLowerName()
+ << "Type)";
+ OS << " " << getLowerName()
+ << "Type->getType().print(OS, Policy);\n";
OS << " OS << \"";
}
More information about the cfe-commits
mailing list