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