1 //===- unittests/AST/EvaluateAsRValueTest.cpp -----------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // \file 10 // \brief Unit tests for evaluation of constant initializers. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/AST/ASTConsumer.h" 15 #include "clang/AST/ASTContext.h" 16 #include "clang/AST/DynamicRecursiveASTVisitor.h" 17 #include "clang/Tooling/Tooling.h" 18 #include "gtest/gtest.h" 19 #include <map> 20 #include <string> 21 22 using namespace clang::tooling; 23 24 namespace { 25 // For each variable name encountered, whether its initializer was a 26 // constant. 27 typedef std::map<std::string, bool> VarInfoMap; 28 29 /// \brief Records information on variable initializers to a map. 30 class EvaluateConstantInitializersVisitor 31 : public clang::DynamicRecursiveASTVisitor { 32 public: 33 explicit EvaluateConstantInitializersVisitor(VarInfoMap &VarInfo) 34 : VarInfo(VarInfo) {} 35 36 /// \brief Checks that isConstantInitializer and EvaluateAsRValue agree 37 /// and don't crash. 38 /// 39 /// For each VarDecl with an initializer this also records in VarInfo 40 /// whether the initializer could be evaluated as a constant. 41 bool VisitVarDecl(clang::VarDecl *VD) override { 42 if (const clang::Expr *Init = VD->getInit()) { 43 clang::Expr::EvalResult Result; 44 bool WasEvaluated = Init->EvaluateAsRValue(Result, VD->getASTContext()); 45 VarInfo[VD->getNameAsString()] = WasEvaluated; 46 EXPECT_EQ(WasEvaluated, Init->isConstantInitializer(VD->getASTContext(), 47 false /*ForRef*/)); 48 } 49 return true; 50 } 51 52 private: 53 VarInfoMap &VarInfo; 54 }; 55 56 class EvaluateConstantInitializersAction : public clang::ASTFrontendAction { 57 public: 58 std::unique_ptr<clang::ASTConsumer> 59 CreateASTConsumer(clang::CompilerInstance &Compiler, 60 llvm::StringRef FilePath) override { 61 return std::make_unique<Consumer>(); 62 } 63 64 private: 65 class Consumer : public clang::ASTConsumer { 66 public: 67 ~Consumer() override {} 68 69 void HandleTranslationUnit(clang::ASTContext &Ctx) override { 70 VarInfoMap VarInfo; 71 EvaluateConstantInitializersVisitor Evaluator(VarInfo); 72 Evaluator.TraverseDecl(Ctx.getTranslationUnitDecl()); 73 EXPECT_EQ(2u, VarInfo.size()); 74 EXPECT_FALSE(VarInfo["Dependent"]); 75 EXPECT_TRUE(VarInfo["Constant"]); 76 EXPECT_EQ(2u, VarInfo.size()); 77 } 78 }; 79 }; 80 } 81 82 TEST(EvaluateAsRValue, FailsGracefullyForUnknownTypes) { 83 // This is a regression test; the AST library used to trigger assertion 84 // failures because it assumed that the type of initializers was always 85 // known (which is true only after template instantiation). 86 std::string ModesToTest[] = {"-std=c++03", "-std=c++11", "-std=c++1y"}; 87 for (std::string const &Mode : ModesToTest) { 88 std::vector<std::string> Args(1, Mode); 89 Args.push_back("-fno-delayed-template-parsing"); 90 ASSERT_TRUE(runToolOnCodeWithArgs( 91 std::make_unique<EvaluateConstantInitializersAction>(), 92 "template <typename T>" 93 "struct vector {" 94 " explicit vector(int size);" 95 "};" 96 "template <typename R>" 97 "struct S {" 98 " vector<R> intervals() const {" 99 " vector<R> Dependent(2);" 100 " return Dependent;" 101 " }" 102 "};" 103 "void doSomething() {" 104 " int Constant = 2 + 2;" 105 " (void) Constant;" 106 "}", 107 Args)); 108 } 109 } 110 111 class CheckLValueToRValueConversionVisitor 112 : public clang::DynamicRecursiveASTVisitor { 113 public: 114 bool VisitDeclRefExpr(clang::DeclRefExpr *E) override { 115 clang::Expr::EvalResult Result; 116 E->EvaluateAsRValue(Result, E->getDecl()->getASTContext(), true); 117 118 EXPECT_TRUE(Result.Val.hasValue()); 119 // Since EvaluateAsRValue does an implicit lvalue-to-rvalue conversion, 120 // the result cannot be a LValue. 121 EXPECT_FALSE(Result.Val.isLValue()); 122 123 return true; 124 } 125 }; 126 127 class CheckConversionAction : public clang::ASTFrontendAction { 128 public: 129 std::unique_ptr<clang::ASTConsumer> 130 CreateASTConsumer(clang::CompilerInstance &Compiler, 131 llvm::StringRef FilePath) override { 132 return std::make_unique<Consumer>(); 133 } 134 135 private: 136 class Consumer : public clang::ASTConsumer { 137 public: 138 ~Consumer() override {} 139 140 void HandleTranslationUnit(clang::ASTContext &Ctx) override { 141 CheckLValueToRValueConversionVisitor Evaluator; 142 Evaluator.TraverseDecl(Ctx.getTranslationUnitDecl()); 143 } 144 }; 145 }; 146 147 TEST(EvaluateAsRValue, LValueToRValueConversionWorks) { 148 std::string ModesToTest[] = {"", "-fexperimental-new-constant-interpreter"}; 149 for (std::string const &Mode : ModesToTest) { 150 std::vector<std::string> Args(1, Mode); 151 ASSERT_TRUE(runToolOnCodeWithArgs(std::make_unique<CheckConversionAction>(), 152 "constexpr int a = 20;\n" 153 "static_assert(a == 20, \"\");\n", 154 Args)); 155 } 156 } 157