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/RecursiveASTVisitor.h"
17#include "clang/Tooling/Tooling.h"
18#include "gtest/gtest.h"
19#include <map>
20#include <string>
21
22using namespace clang::tooling;
23
24namespace {
25// For each variable name encountered, whether its initializer was a
26// constant.
27typedef std::map<std::string, bool> VarInfoMap;
28
29/// \brief Records information on variable initializers to a map.
30class EvaluateConstantInitializersVisitor
31 : public clang::RecursiveASTVisitor<EvaluateConstantInitializersVisitor> {
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(const clang::VarDecl *VD) {
42 if (const clang::Expr *Init = VD->getInit()) {
43 clang::Expr::EvalResult Result;
44 bool WasEvaluated = Init->EvaluateAsRValue(Result, Ctx: 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
56class 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
82TEST(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(x: "-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
111class CheckLValueToRValueConversionVisitor
112 : public clang::RecursiveASTVisitor<CheckLValueToRValueConversionVisitor> {
113public:
114 bool VisitDeclRefExpr(const clang::DeclRefExpr *E) {
115 clang::Expr::EvalResult Result;
116 E->EvaluateAsRValue(Result, Ctx: E->getDecl()->getASTContext(), InConstantContext: 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
127class CheckConversionAction : public clang::ASTFrontendAction {
128public:
129 std::unique_ptr<clang::ASTConsumer>
130 CreateASTConsumer(clang::CompilerInstance &Compiler,
131 llvm::StringRef FilePath) override {
132 return std::make_unique<Consumer>();
133 }
134
135private:
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
147TEST(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

source code of clang/unittests/AST/EvaluateAsRValueTest.cpp