diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2007,6 +2007,36 @@ 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 Args, @@ -2025,15 +2055,8 @@ } } - 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 --- a/clang/unittests/AST/TypePrinterTest.cpp +++ b/clang/unittests/AST/TypePrinterTest.cpp @@ -127,3 +127,131 @@ 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 + struct Foo { + }; + + Foo X; + )cpp"; + + auto AST = tooling::buildASTFromCodeWithArgs(Code, /*Args=*/{"-std=c++20"}); + ASTContext &Ctx = AST->getASTContext(); + + auto const *CTD = selectFirst( + "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(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())); + } +}