leonardchan created this revision.
leonardchan added reviewers: rsmith, rnk, echristo.
leonardchan added a project: clang.
Herald added a subscriber: jeroen.dobbelaere.
Herald added a project: All.
leonardchan requested review of this revision.
Prior to this, the following would fail:
template <typename T>
using NaturallyAligned [[gnu::aligned(sizeof(T))]] = T;
struct S { char x[2]; };
using AlignedS = NaturallyAligned<S>;
static_assert(alignof(AlignedS) == 2); // alignof(AlignedS) == 1
This is because clang ignores attributes on type aliases when evaluating type
info and instead resolves to alignment of the replacement type `T`.
This patch attempts to check if the associated type declaration is an alias
that contains an attribute and return whatever the value is indicated by the
alignment expression.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D143533
Files:
clang/lib/AST/ASTContext.cpp
clang/test/SemaTemplate/type-alias-aligned.cpp
Index: clang/test/SemaTemplate/type-alias-aligned.cpp
===================================================================
--- /dev/null
+++ 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 <typename T>
+using Aligned8 [[gnu::aligned(8)]] = T;
+
+template <typename T>
+using NaturallyAligned [[gnu::aligned(sizeof(T))]] = T;
+
+template <typename T>
+using UnderAligned [[gnu::aligned(1)]] = T;
+
+template <typename T, unsigned S = sizeof(T)>
+using NaturallyAligned2 [[gnu::aligned(S)]] = T;
+
+template <typename T>
+using UnaryAligned [[gnu::aligned(+sizeof(T))]] = T;
+
+template <typename T>
+using BinaryAligned [[gnu::aligned(sizeof(T) * 2)]] = T;
+
+struct S { unsigned x[2]; };
+static_assert(alignof(S) == 4);
+
+using Aligned8_S = Aligned8<S>;
+static_assert(alignof(Aligned8_S) == 8);
+
+using NatAligned_S = NaturallyAligned<S>;
+static_assert(alignof(NatAligned_S) == 8);
+
+using UnderAligned_S = UnderAligned<S>;
+static_assert(alignof(UnderAligned_S) == 1);
+
+using NatAligned_S2 = NaturallyAligned2<S>;
+static_assert(alignof(NatAligned_S2) == 8);
+
+using UnaryAligned_S = UnaryAligned<S>;
+static_assert(alignof(UnaryAligned_S) == 8);
+
+using BinaryAligned_S = BinaryAligned<S>;
+static_assert(alignof(BinaryAligned_S) == 16);
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ 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<SubstTypeVisitor, /*RetTy=*/Expr *> {
+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<NonTypeTemplateParmDecl>(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 <typename T>
+/// 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<TemplateDecl>(SubType->getAssociatedDecl())))
+ return false;
+
+ if (!TD->isTypeAlias())
+ return false;
+
+ const TypeAliasDecl *TAD;
+ if (!(TAD = dyn_cast<TypeAliasDecl>(TD->getTemplatedDecl())))
+ return false;
+
+ const AlignedAttr *Attr;
+ if (!(Attr = TAD->getAttr<AlignedAttr>()))
+ 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<SubstTemplateTypeParmType>(T)->
- getReplacementType().getTypePtr());
-
+ case Type::SubstTemplateTypeParm: {
+ const auto *SubType = cast<SubstTemplateTypeParmType>(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<DeducedType>(T);
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits