From f5ba472e84dcf35c25993f7d8198c2aea179acff Mon Sep 17 00:00:00 2001 From: James Dennett Date: Fri, 14 Mar 2014 17:44:10 +0000 Subject: [PATCH] Fix a crash (assertion failure) in EvaluateAsRValue. Summary: Gracefully fail to evaluate a constant expression if its type is unknown, rather than failing an assertion trying to access the type. Reviewers: klimek Reviewed By: klimek CC: chandlerc, cfe-commits Differential Revision: http://llvm-reviews.chandlerc.com/D3075 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@203950 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 10 +++ unittests/AST/CMakeLists.txt | 1 + unittests/AST/EvaluateAsRValueTest.cpp | 112 +++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 unittests/AST/EvaluateAsRValueTest.cpp diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index b6e8b6dcf6..4c98c00474 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -8034,6 +8034,9 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, /// EvaluateAsRValue - Try to evaluate this expression, performing an implicit /// lvalue-to-rvalue cast if it is an lvalue. static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { + if (E->getType().isNull()) + return false; + if (!CheckLiteralType(Info, E)) return false; @@ -8061,6 +8064,13 @@ static bool FastEvaluateAsRValue(const Expr *Exp, Expr::EvalResult &Result, IsConst = true; return true; } + + // This case should be rare, but we need to check it before we check on + // the type below. + if (Exp->getType().isNull()) { + IsConst = false; + return true; + } // FIXME: Evaluating values of large array and record types can cause // performance problems. Only do so in C++11 for now. diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt index 4ad3b1af6f..9a55626ae4 100644 --- a/unittests/AST/CMakeLists.txt +++ b/unittests/AST/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_unittest(ASTTests CommentParser.cpp DeclPrinterTest.cpp DeclTest.cpp + EvaluateAsRValueTest.cpp ExternalASTSourceTest.cpp SourceLocationTest.cpp StmtPrinterTest.cpp diff --git a/unittests/AST/EvaluateAsRValueTest.cpp b/unittests/AST/EvaluateAsRValueTest.cpp new file mode 100644 index 0000000000..5f45626567 --- /dev/null +++ b/unittests/AST/EvaluateAsRValueTest.cpp @@ -0,0 +1,112 @@ +//===- unittests/AST/EvaluateAsRValueTest.cpp -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// \file +// \brief Unit tests for evaluation of constant initializers. +// +//===----------------------------------------------------------------------===// + +#include +#include + +#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 "clang/AST/ASTConsumer.h" + +using namespace clang::tooling; + +namespace { +// For each variable name encountered, whether its initializer was a +// constant. +typedef std::map VarInfoMap; + +/// \brief Records information on variable initializers to a map. +class EvaluateConstantInitializersVisitor + : public clang::RecursiveASTVisitor { + 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: + clang::ASTConsumer *CreateASTConsumer(clang::CompilerInstance &Compiler, + llvm::StringRef FilePath) override { + return new 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 Args(1, Mode); + ASSERT_TRUE(runToolOnCodeWithArgs( + new EvaluateConstantInitializersAction(), + R"(template + struct vector { + explicit vector(int size); + }; + template + struct S { + vector intervals() const { + vector Dependent(2); + return Dependent; + } + }; + void doSomething() { + int Constant = 2 + 2; + (void) Constant; + } + )", + Args)); + } +} -- 2.40.0