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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D143533: ... Leonard Chan via Phabricator via cfe-commits

Reply via email to