diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -41,6 +41,7 @@ #include "clang/AST/RawCommentList.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Stmt.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" @@ -2026,6 +2027,138 @@ return TI; } +/// This class attempts to substitute the template parameter for a TypeAliasDecl +/// with the replacement type for a SubstTemplateTypeParmType in order to create +/// a new expression that is evaluatable (ie. not value-dependent). We need to +/// create a new expression rather than replace componenets of an existing +/// expression to recompute `dependence`s. Normally, this would be done via the +/// TemplateDeclInstantiator, but that requires a reference to Sema which may +/// not be available since the TypeInfo machinery only operates on the +/// ASTContext. +/// +/// NOTE: This class should be updated for various dependent expressions that +/// can appear in an AlignedAttr's alignment expression. This can be done by +/// adding more Visit methods. +/// +class SubstTypeVisitor + : public StmtVisitor { +public: + SubstTypeVisitor(ASTContext &Ctx, QualType ReplacementTy) + : Ctx(Ctx), ReplacementTy(ReplacementTy) {} + + uint64_t TryEval(Expr *E) { + Expr *NewE = Visit(E); + if (!NewE || NewE->isValueDependent()) + return 0; + return NewE->EvaluateKnownConstInt(Ctx).getZExtValue() * Ctx.getCharWidth(); + } + + Expr *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { + return new (Ctx) UnaryExprOrTypeTraitExpr( + E->getKind(), Ctx.CreateTypeSourceInfo(ReplacementTy), E->getType(), + E->getBeginLoc(), E->getEndLoc()); + } + + Expr *VisitDeclRefExpr(DeclRefExpr *E) { + if (const auto *NTPD = + dyn_cast(E->getFoundDecl())) { + if (Expr *DefaultArg = NTPD->getDefaultArgument()) + return Visit(DefaultArg); + } + return E; + } + + Expr *VisitUnaryOperator(UnaryOperator *E) { + Expr *SubExpr = Visit(E->getSubExpr()); + if (!SubExpr) + return nullptr; + return UnaryOperator::Create(Ctx, SubExpr, E->getOpcode(), E->getType(), + E->getValueKind(), E->getObjectKind(), + E->getExprLoc(), E->canOverflow(), + E->getFPOptionsOverride()); + } + + Expr *VisitImplicitCastExpr(ImplicitCastExpr *E) { + Expr *SubExpr = Visit(E->getSubExpr()); + if (!SubExpr) + return nullptr; + return ImplicitCastExpr::Create(Ctx, E->getType(), E->getCastKind(), + SubExpr, nullptr, E->getValueKind(), + E->getFPFeatures()); + } + + Expr *VisitBinaryOperator(BinaryOperator *E) { + Expr *LHS = Visit(E->getLHS()); + if (!LHS) + return nullptr; + Expr *RHS = Visit(E->getRHS()); + if (!RHS) + return nullptr; + return BinaryOperator::Create(Ctx, LHS, RHS, E->getOpcode(), E->getType(), + E->getValueKind(), E->getObjectKind(), + E->getExprLoc(), E->getFPFeatures()); + } + + Expr *VisitExpr(Expr *E) { return E; } + +private: + ASTContext &Ctx; + QualType ReplacementTy; +}; + +/// If the SubstTemplateTypeParm type points to a template alias declaration, +/// then we can try to substitute the template parameter with the replacement +/// type and evaluate the alignment from there. This is needed for alignment +/// attributes where the alignment expression is not evaluatable because it has +/// a value-dependent type. An example of this is: +/// +/// template +/// using NaturallyAligned [[gnu::aligned(sizeof(T))]] = T; +/// +/// where the expression `sizeof(T)` cannot be evaluated yet unless `T` is +/// known. For a SubstTemplateTypeParmType, we can substitute `T` for the +/// replacement type and try to evaluate `sizeof(T)` from here. If the +/// expression is still not evaluatable, default to the alignment of the +/// replacement type. +static bool +MaybeGetAlignForTemplateAlias(TypeInfo &TI, + const SubstTemplateTypeParmType *SubType) { + // We only want to cover the case where the type revers to an alias with an + // AlignedAttr. + const TemplateDecl *TD; + if (!(TD = dyn_cast(SubType->getAssociatedDecl()))) + return false; + + if (!TD->isTypeAlias()) + return false; + + const TypeAliasDecl *TAD; + if (!(TAD = dyn_cast(TD->getTemplatedDecl()))) + return false; + + const AlignedAttr *Attr; + if (!(Attr = TAD->getAttr())) + return false; + + auto &Ctx = TD->getASTContext(); + + // Short circuit case: the expression is immediately evaluatable. + if (!Attr->getAlignmentExpr()->isValueDependent()) { + TI.Align = Attr->getAlignment(Ctx); + return true; + } + + // Otherwise, try to see if we can substitute any template parameters with the + // replacement type. + if (uint64_t Result = SubstTypeVisitor(Ctx, SubType->getReplacementType()) + .TryEval(Attr->getAlignmentExpr())) { + TI.Align = Result; + return true; + } + + return false; +} + /// getTypeInfoImpl - Return the size of the specified type, in bits. This /// method does not work on incomplete types. /// @@ -2441,10 +2574,13 @@ break; } - case Type::SubstTemplateTypeParm: - return getTypeInfo(cast(T)-> - getReplacementType().getTypePtr()); - + case Type::SubstTemplateTypeParm: { + const auto *SubType = cast(T); + auto TI = getTypeInfo(SubType->getReplacementType().getTypePtr()); + if (MaybeGetAlignForTemplateAlias(TI, SubType)) + TI.AlignRequirement = AlignRequirementKind::RequiredByTypedef; + return TI; + } case Type::Auto: case Type::DeducedTemplateSpecialization: { const auto *A = cast(T); diff --git a/clang/test/SemaTemplate/type-alias-aligned.cpp b/clang/test/SemaTemplate/type-alias-aligned.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaTemplate/type-alias-aligned.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++17 -triple=x86_64-linux-gnu +// expected-no-diagnostics + +template +using Aligned8 [[gnu::aligned(8)]] = T; + +template +using NaturallyAligned [[gnu::aligned(sizeof(T))]] = T; + +template +using UnderAligned [[gnu::aligned(1)]] = T; + +template +using NaturallyAligned2 [[gnu::aligned(S)]] = T; + +template +using UnaryAligned [[gnu::aligned(+sizeof(T))]] = T; + +template +using BinaryAligned [[gnu::aligned(sizeof(T) * 2)]] = T; + +struct S { unsigned x[2]; }; +static_assert(alignof(S) == 4); + +using Aligned8_S = Aligned8; +static_assert(alignof(Aligned8_S) == 8); + +using NatAligned_S = NaturallyAligned; +static_assert(alignof(NatAligned_S) == 8); + +using UnderAligned_S = UnderAligned; +static_assert(alignof(UnderAligned_S) == 1); + +using NatAligned_S2 = NaturallyAligned2; +static_assert(alignof(NatAligned_S2) == 8); + +using UnaryAligned_S = UnaryAligned; +static_assert(alignof(UnaryAligned_S) == 8); + +using BinaryAligned_S = BinaryAligned; +static_assert(alignof(BinaryAligned_S) == 16);