[clang] 798494e - [clang][TypePrinter] Support expression template arguments when checking defaultedness

Michael Buch via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 26 18:35:10 PST 2023


Author: Michael Buch
Date: 2023-01-27T02:24:32Z
New Revision: 798494ed4f112bf64dcabbe8b60becb42b23208f

URL: https://github.com/llvm/llvm-project/commit/798494ed4f112bf64dcabbe8b60becb42b23208f
DIFF: https://github.com/llvm/llvm-project/commit/798494ed4f112bf64dcabbe8b60becb42b23208f.diff

LOG: [clang][TypePrinter] Support expression template arguments when checking defaultedness

This patch adds support for `TemplateArgument`s of kind
`TemplateArgument::Expression` to `clang::isSubstitutedDefaultArgument`.
We do so by evaluating both the `Pattern` and `Arg` expression to an
`APInt`, if we can, and comparing the results.

This will be useful in an upcoming change where
`clang::isSubstitutedDefaultArgument` gets called from `clang::Sema`
where the `TemplateArgument`s are instantiated as expressions (without
being evaluted to `APInt` beforehand).

**Testing**

- Added unit-tests

Differential Revision: https://reviews.llvm.org/D142632

Added: 
    

Modified: 
    clang/lib/AST/TypePrinter.cpp
    clang/unittests/AST/TypePrinterTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 5c24649044856..b710bab0eb972 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2007,6 +2007,36 @@ static bool isSubstitutedType(ASTContext &Ctx, QualType T, QualType Pattern,
   return false;
 }
 
+/// Evaluates the expression template argument 'Pattern' and returns true
+/// if 'Arg' evaluates to the same result.
+static bool templateArgumentExpressionsEqual(ASTContext const &Ctx,
+                                             TemplateArgument const &Pattern,
+                                             TemplateArgument const &Arg) {
+  if (Pattern.getKind() != TemplateArgument::Expression)
+    return false;
+
+  // Can't evaluate value-dependent expressions so bail early
+  Expr const *pattern_expr = Pattern.getAsExpr();
+  if (pattern_expr->isValueDependent() ||
+      !pattern_expr->isIntegerConstantExpr(Ctx))
+    return false;
+
+  if (Arg.getKind() == TemplateArgument::Integral)
+    return llvm::APSInt::isSameValue(pattern_expr->EvaluateKnownConstInt(Ctx),
+                                     Arg.getAsIntegral());
+
+  if (Arg.getKind() == TemplateArgument::Expression) {
+    Expr const *args_expr = Arg.getAsExpr();
+    if (args_expr->isValueDependent() || !args_expr->isIntegerConstantExpr(Ctx))
+      return false;
+
+    return llvm::APSInt::isSameValue(args_expr->EvaluateKnownConstInt(Ctx),
+                                     pattern_expr->EvaluateKnownConstInt(Ctx));
+  }
+
+  return false;
+}
+
 static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg,
                                           TemplateArgument Pattern,
                                           ArrayRef<TemplateArgument> Args,
@@ -2025,15 +2055,8 @@ static bool isSubstitutedTemplateArgument(ASTContext &Ctx, TemplateArgument Arg,
     }
   }
 
-  if (Arg.getKind() == TemplateArgument::Integral &&
-      Pattern.getKind() == TemplateArgument::Expression) {
-    Expr const *expr = Pattern.getAsExpr();
-
-    if (!expr->isValueDependent() && expr->isIntegerConstantExpr(Ctx)) {
-      return llvm::APSInt::isSameValue(expr->EvaluateKnownConstInt(Ctx),
-                                       Arg.getAsIntegral());
-    }
-  }
+  if (templateArgumentExpressionsEqual(Ctx, Pattern, Arg))
+    return true;
 
   if (Arg.getKind() != Pattern.getKind())
     return false;

diff  --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp
index fe20925ea6b50..77ff442236686 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -127,3 +127,131 @@ TEST(TypePrinter, TemplateIdWithNTTP) {
         Policy.EntireContentsOfLargeArray = true;
       }));
 }
+
+TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
+  /// Tests clang::isSubstitutedDefaultArgument on TemplateArguments
+  /// that are of kind TemplateArgument::Expression
+  constexpr char Code[] = R"cpp(
+    constexpr bool func() { return true; }
+
+    template <typename T1 = int,
+              int      T2 = 42,
+              T1       T3 = 43,
+              int      T4 = sizeof(T1),
+              bool     T5 = func()
+              >
+    struct Foo {
+    };
+
+    Foo<int, 40 + 2> X;
+  )cpp";
+
+  auto AST = tooling::buildASTFromCodeWithArgs(Code, /*Args=*/{"-std=c++20"});
+  ASTContext &Ctx = AST->getASTContext();
+
+  auto const *CTD = selectFirst<ClassTemplateDecl>(
+      "id", match(classTemplateDecl(hasName("Foo")).bind("id"), Ctx));
+  ASSERT_NE(CTD, nullptr);
+  auto const *CTSD = *CTD->specializations().begin();
+  ASSERT_NE(CTSD, nullptr);
+  auto const *Params = CTD->getTemplateParameters();
+  ASSERT_NE(Params, nullptr);
+  auto const &ArgList = CTSD->getTemplateArgs();
+
+  auto createBinOpExpr = [&](uint32_t LHS, uint32_t RHS,
+                             uint32_t Result) -> ConstantExpr * {
+    const int numBits = 32;
+    clang::APValue ResultVal{llvm::APSInt(llvm::APInt(numBits, Result))};
+    auto *LHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, LHS),
+                                          Ctx.UnsignedIntTy, {});
+    auto *RHSInt = IntegerLiteral::Create(Ctx, llvm::APInt(numBits, RHS),
+                                          Ctx.UnsignedIntTy, {});
+    auto *BinOp = BinaryOperator::Create(
+        Ctx, LHSInt, RHSInt, BinaryOperatorKind::BO_Add, Ctx.UnsignedIntTy,
+        ExprValueKind::VK_PRValue, ExprObjectKind::OK_Ordinary, {}, {});
+    return ConstantExpr::Create(Ctx, dyn_cast<Expr>(BinOp), ResultVal);
+  };
+
+  {
+    // Arg is an integral '42'
+    auto const &Arg = ArgList.get(1);
+    ASSERT_EQ(Arg.getKind(), TemplateArgument::Integral);
+
+    // Param has default expr which evaluates to '42'
+    auto const *Param = Params->getParam(1);
+
+    EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
+        Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
+  }
+
+  {
+    // Arg is an integral '41'
+    llvm::APInt Int(32, 41);
+    TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);
+
+    // Param has default expr which evaluates to '42'
+    auto const *Param = Params->getParam(1);
+
+    EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
+        Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
+  }
+
+  {
+    // Arg is an integral '4'
+    llvm::APInt Int(32, 4);
+    TemplateArgument Arg(Ctx, llvm::APSInt(Int), Ctx.UnsignedIntTy);
+
+    // Param has is value-dependent expression (i.e., sizeof(T))
+    auto const *Param = Params->getParam(3);
+
+    EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
+        Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
+  }
+
+  {
+    const int LHS = 40;
+    const int RHS = 2;
+    const int Result = 42;
+    auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
+    // Arg is instantiated with '40 + 2'
+    TemplateArgument Arg(ConstExpr);
+
+    // Param has default expr of '42'
+    auto const *Param = Params->getParam(1);
+
+    EXPECT_TRUE(clang::isSubstitutedDefaultArgument(
+        Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
+  }
+
+  {
+    const int LHS = 40;
+    const int RHS = 1;
+    const int Result = 41;
+    auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
+
+    // Arg is instantiated with '40 + 1'
+    TemplateArgument Arg(ConstExpr);
+
+    // Param has default expr of '42'
+    auto const *Param = Params->getParam(1);
+
+    EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
+        Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
+  }
+
+  {
+    const int LHS = 4;
+    const int RHS = 0;
+    const int Result = 4;
+    auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
+
+    // Arg is instantiated with '4 + 0'
+    TemplateArgument Arg(ConstExpr);
+
+    // Param has is value-dependent expression (i.e., sizeof(T))
+    auto const *Param = Params->getParam(3);
+
+    EXPECT_FALSE(clang::isSubstitutedDefaultArgument(
+        Ctx, Arg, Param, ArgList.asArray(), Params->getDepth()));
+  }
+}


        


More information about the cfe-commits mailing list