[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