diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -256,6 +256,9 @@ 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 @@ (`#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: `_, 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 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3004,8 +3004,9 @@ 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 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -273,12 +273,13 @@ /// 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 @@ SourceRange attrRange, IdentifierInfo *scopeName, SourceLocation scopeLoc, ParsedType typeArg, - ParsedAttr::Form formUsed) { + ParsedAttr::Form formUsed, + SourceLocation ellipsisLoc) { void *memory = allocate( ParsedAttr::totalSizeToAlloc(0, 0, 0, 1, 0)); return add(new (memory) ParsedAttr(attrName, attrRange, scopeName, scopeLoc, - typeArg, formUsed)); + typeArg, formUsed, ellipsisLoc)); } ParsedAttr * @@ -1001,9 +1003,11 @@ /// 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 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5720,6 +5720,11 @@ 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 @@ 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 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -239,4 +239,31 @@ } } +unsigned AlignedAttr::getAlignment(ASTContext &Ctx) const { + assert(!isAlignmentDependent()); + if (getCachedAlignmentValue()) + return *getCachedAlignmentValue(); + + if (isAlignmentExpr()) { + return alignmentExpr + ? alignmentExpr->EvaluateKnownConstInt(Ctx).getZExtValue() * + Ctx.getCharWidth() + : Ctx.getTargetDefaultAlignForAttributeAligned(); + } + + 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. + if (const ReferenceType *Ref = T->getAs()) + T = Ref->getPointeeType(); + + if (T.getQualifiers().hasUnaligned()) + return Ctx.getCharWidth(); + + return Ctx.getTypeAlignInChars(T.getTypePtr()).getQuantity() * + Ctx.getCharWidth(); +} + #include "clang/AST/AttrImpl.inc" diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2984,24 +2984,26 @@ /// 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 @@ 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 @@ 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 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4333,6 +4333,27 @@ } 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 @@ 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(D)) { + DiagKind = 0; + } else if (const auto *VD = dyn_cast(D)) { + if (VD->getStorageClass() == SC_Register) + DiagKind = 1; + if (VD->isExceptionVariable()) + DiagKind = 2; + } else if (const auto *FD = dyn_cast(D)) { + if (FD->isBitField()) + DiagKind = 3; + } else if (const auto *ED = dyn_cast(D)) { + if (ED->getLangOpts().CPlusPlus) + DiagKind = 4; + } else if (!isa(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(D)) { - DiagKind = 0; - } else if (const auto *VD = dyn_cast(D)) { - if (VD->getStorageClass() == SC_Register) - DiagKind = 1; - if (VD->isExceptionVariable()) - DiagKind = 2; - } else if (const auto *FD = dyn_cast(D)) { - if (FD->isBitField()) - DiagKind = 3; - } else if (const auto *ED = dyn_cast(D)) { - if (ED->getLangOpts().CPlusPlus) - DiagKind = 4; - } else if (!isa(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 @@ AlignedAttr *AA = ::new (Context) AlignedAttr(Context, CI, true, ICE.get()); AA->setPackExpansion(IsPackExpansion); + AA->setCachedAlignmentValue( + static_cast(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(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(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 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4355,70 +4355,6 @@ 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()) - 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 @@ } 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()) + 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()) { + if (ExprType->isVariablyModifiedType() && FunctionScopes.size() > 1) { + if (auto *TT = ExprType->getAs()) { for (auto I = FunctionScopes.rbegin(), E = std::prev(FunctionScopes.rend()); I != E; ++I) { @@ -4629,17 +4611,37 @@ 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 @@ 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 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -95,11 +95,14 @@ if (!Result.isInvalid()) S.AddAlignedAttr(New, *Aligned, Result.getAs(), 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 --- a/clang/test/AST/ast-dump-attr.cpp +++ b/clang/test/AST/ast-dump-attr.cpp @@ -129,6 +129,38 @@ // CHECK-NEXT: AlignedAttr{{.*}} Inherited } +namespace TestAligns { + +template struct my_union { + alignas(T...) char buffer[1024]; +}; + +template 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 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 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 --- a/clang/test/Sema/aix-attr-aligned-vector-warn.c +++ b/clang/test/Sema/aix-attr-aligned-vector-warn.c @@ -6,6 +6,7 @@ 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 --- /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 --- a/clang/test/Sema/sizeless-1.c +++ b/clang/test/Sema/sizeless-1.c @@ -64,7 +64,7 @@ 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 --- a/clang/test/SemaCXX/attr-cxx0x.cpp +++ b/clang/test/SemaCXX/attr-cxx0x.cpp @@ -50,3 +50,6 @@ 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 --- 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(7), static_cast(4)), ""); static_assert(!__builtin_is_aligned(static_cast(7), static_cast(4)), ""); static_assert(!__builtin_is_aligned(static_cast(7), static_cast(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 --- a/clang/test/SemaCXX/cxx11-attr-print.cpp +++ b/clang/test/SemaCXX/cxx11-attr-print.cpp @@ -28,7 +28,7 @@ // 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 @@ // 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 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 --- a/clang/test/SemaCXX/sizeless-1.cpp +++ b/clang/test/SemaCXX/sizeless-1.cpp @@ -73,7 +73,7 @@ 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 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -508,6 +508,16 @@ OS << " assert(!is" << getLowerName() << "Expr);\n"; OS << " return " << getLowerName() << "Type;\n"; OS << " }"; + + OS << " std::optional 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 @@ 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 @@ OS << "union {\n"; OS << "Expr *" << getLowerName() << "Expr;\n"; OS << "TypeSourceInfo *" << getLowerName() << "Type;\n"; - OS << "};"; + OS << "};\n"; + OS << "std::optional " << getLowerName() << "Cache;\n"; } void writePCHReadArgs(raw_ostream &OS) const override { @@ -628,14 +624,21 @@ } 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 << \""; }