xref: /llvm-project/clang/unittests/AST/EvaluateAsRValueTest.cpp (revision 4e600751d2f7e8e7b85a71b7128b68444bdde91b)
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