
The EvaluateAsRValue() documentation mentions that an implicit lvalue-to-rvalue cast is being performed if the result is an lvalue. However, that was not being done if the new constant interpreter was in use. Just always do it. Differential Revision: https://reviews.llvm.org/D132136
157 lines
5.2 KiB
C++
157 lines
5.2 KiB
C++
//===- 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));
|
|
}
|
|
}
|