[llvm] 23ac16c - FileCheck [10/12]: Add support for signed numeric values
Thomas Preud'homme via llvm-commits
llvm-commits at lists.llvm.org
Thu May 28 02:45:49 PDT 2020
Author: Thomas Preud'homme
Date: 2020-05-28T10:44:21+01:00
New Revision: 23ac16cf9bd4cc0bb434efcf6385baf083a2ff7b
URL: https://github.com/llvm/llvm-project/commit/23ac16cf9bd4cc0bb434efcf6385baf083a2ff7b
DIFF: https://github.com/llvm/llvm-project/commit/23ac16cf9bd4cc0bb434efcf6385baf083a2ff7b.diff
LOG: FileCheck [10/12]: Add support for signed numeric values
Summary:
This patch is part of a patch series to add support for FileCheck
numeric expressions. This specific patch adds support signed numeric
values, thus allowing negative numeric values.
As such, the patch adds a new class to represent a signed or unsigned
value and add the logic for type promotion and type conversion in
numeric expression mixing signed and unsigned values. It also adds
the %d format specifier to represent signed value.
Finally, it also adds underflow and overflow detection when performing a
binary operation.
Copyright:
- Linaro (changes up to diff 183612 of revision D55940)
- GraphCore (changes in later versions of revision D55940 and
in new revision created off D55940)
Reviewers: jhenderson, chandlerc, jdenny, probinson, grimar, arichardson
Reviewed By: jhenderson, arichardson
Subscribers: MaskRay, hiraditya, llvm-commits, probinson, dblaikie, grimar, arichardson, kristina, hfinkel, rogfer01, JonChesterfield
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D60390
Added:
Modified:
llvm/docs/CommandGuide/FileCheck.rst
llvm/lib/Support/FileCheck.cpp
llvm/lib/Support/FileCheckImpl.h
llvm/test/FileCheck/numeric-expression.txt
llvm/unittests/Support/FileCheckTest.cpp
Removed:
################################################################################
diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst
index d8a2e343026b..0512133f2e99 100644
--- a/llvm/docs/CommandGuide/FileCheck.rst
+++ b/llvm/docs/CommandGuide/FileCheck.rst
@@ -660,8 +660,8 @@ The syntax to define a numeric variable is ``[[#%<fmtspec>,<NUMVAR>:]]`` where:
* ``%<fmtspec>`` is an optional scanf-style matching format specifier to
indicate what number format to match (e.g. hex number). Currently accepted
- format specifiers are ``%u``, ``%x`` and ``%X``. If absent, the format
- specifier defaults to ``%u``.
+ format specifiers are ``%u``, ``%d``, ``%x`` and ``%X``. If absent, the
+ format specifier defaults to ``%u``.
* ``<NUMVAR>`` is the name of the numeric variable to define to the matching
value.
@@ -692,10 +692,11 @@ The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
* an expression followed by an operator and a numeric operand.
A numeric operand is a previously defined numeric variable, or an integer
- literal. The supported operators are ``+`` and ``-``. Spaces are accepted
- before, after and between any of these elements.
- There is currently no support for operator precendence, but parentheses can
- be used to change the evaluation order.
+ literal and have a 64-bit precision. The supported operators are ``+`` and
+ ``-``. Spaces are accepted before, after and between any of these elements.
+ Overflow and underflow are rejected. There is currently no support for
+ operator precendence, but parentheses can be used to change the evaluation
+ order.
For example:
diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp
index 300eea865f91..454f38132f6b 100644
--- a/llvm/lib/Support/FileCheck.cpp
+++ b/llvm/lib/Support/FileCheck.cpp
@@ -17,6 +17,7 @@
#include "FileCheckImpl.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CheckedArithmetic.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <list>
@@ -31,6 +32,8 @@ StringRef ExpressionFormat::toString() const {
return StringRef("<none>");
case Kind::Unsigned:
return StringRef("%u");
+ case Kind::Signed:
+ return StringRef("%d");
case Kind::HexUpper:
return StringRef("%X");
case Kind::HexLower:
@@ -43,6 +46,8 @@ Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
switch (Value) {
case Kind::Unsigned:
return StringRef("[0-9]+");
+ case Kind::Signed:
+ return StringRef("-?[0-9]+");
case Kind::HexUpper:
return StringRef("[0-9A-F]+");
case Kind::HexLower:
@@ -54,43 +59,188 @@ Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
}
Expected<std::string>
-ExpressionFormat::getMatchingString(uint64_t IntegerValue) const {
+ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const {
+ if (Value == Kind::Signed) {
+ Expected<int64_t> SignedValue = IntegerValue.getSignedValue();
+ if (!SignedValue)
+ return SignedValue.takeError();
+ return itostr(*SignedValue);
+ }
+
+ Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue();
+ if (!UnsignedValue)
+ return UnsignedValue.takeError();
switch (Value) {
case Kind::Unsigned:
- return utostr(IntegerValue);
+ return utostr(*UnsignedValue);
case Kind::HexUpper:
- return utohexstr(IntegerValue, /*LowerCase=*/false);
+ return utohexstr(*UnsignedValue, /*LowerCase=*/false);
case Kind::HexLower:
- return utohexstr(IntegerValue, /*LowerCase=*/true);
+ return utohexstr(*UnsignedValue, /*LowerCase=*/true);
default:
return createStringError(std::errc::invalid_argument,
"trying to match value with invalid format");
}
}
-Expected<uint64_t>
+Expected<ExpressionValue>
ExpressionFormat::valueFromStringRepr(StringRef StrVal,
const SourceMgr &SM) const {
+ bool ValueIsSigned = Value == Kind::Signed;
+ StringRef OverflowErrorStr = "unable to represent numeric value";
+ if (ValueIsSigned) {
+ int64_t SignedValue;
+
+ if (StrVal.getAsInteger(10, SignedValue))
+ return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
+
+ return ExpressionValue(SignedValue);
+ }
+
bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
- uint64_t IntegerValue;
- if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue))
- return ErrorDiagnostic::get(SM, StrVal,
- "unable to represent numeric value");
+ uint64_t UnsignedValue;
+ if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue))
+ return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
- return IntegerValue;
+ return ExpressionValue(UnsignedValue);
}
-Expected<uint64_t> NumericVariableUse::eval() const {
- Optional<uint64_t> Value = Variable->getValue();
+static int64_t getAsSigned(uint64_t UnsignedValue) {
+ // Use memcpy to reinterpret the bitpattern in Value since casting to
+ // signed is implementation-defined if the unsigned value is too big to be
+ // represented in the signed type and using an union violates type aliasing
+ // rules.
+ int64_t SignedValue;
+ memcpy(&SignedValue, &UnsignedValue, sizeof(SignedValue));
+ return SignedValue;
+}
+
+Expected<int64_t> ExpressionValue::getSignedValue() const {
+ if (Negative)
+ return getAsSigned(Value);
+
+ if (Value > std::numeric_limits<int64_t>::max())
+ return make_error<OverflowError>();
+
+ // Value is in the representable range of int64_t so we can use cast.
+ return static_cast<int64_t>(Value);
+}
+
+Expected<uint64_t> ExpressionValue::getUnsignedValue() const {
+ if (Negative)
+ return make_error<OverflowError>();
+
+ return Value;
+}
+
+ExpressionValue ExpressionValue::getAbsolute() const {
+ if (!Negative)
+ return *this;
+
+ int64_t SignedValue = getAsSigned(Value);
+ int64_t MaxInt64 = std::numeric_limits<int64_t>::max();
+ // Absolute value can be represented as int64_t.
+ if (SignedValue >= -MaxInt64)
+ return ExpressionValue(-getAsSigned(Value));
+
+ // -X == -(max int64_t + Rem), negate each component independently.
+ SignedValue += MaxInt64;
+ uint64_t RemainingValueAbsolute = -SignedValue;
+ return ExpressionValue(MaxInt64 + RemainingValueAbsolute);
+}
+
+Expected<ExpressionValue> llvm::operator+(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ if (LeftOperand.isNegative() && RightOperand.isNegative()) {
+ int64_t LeftValue = cantFail(LeftOperand.getSignedValue());
+ int64_t RightValue = cantFail(RightOperand.getSignedValue());
+ Optional<int64_t> Result = checkedAdd<int64_t>(LeftValue, RightValue);
+ if (!Result)
+ return make_error<OverflowError>();
+
+ return ExpressionValue(*Result);
+ }
+
+ // (-A) + B == B - A.
+ if (LeftOperand.isNegative())
+ return RightOperand - LeftOperand.getAbsolute();
+
+ // A + (-B) == A - B.
+ if (RightOperand.isNegative())
+ return LeftOperand - RightOperand.getAbsolute();
+
+ // Both values are positive at this point.
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ Optional<uint64_t> Result =
+ checkedAddUnsigned<uint64_t>(LeftValue, RightValue);
+ if (!Result)
+ return make_error<OverflowError>();
+
+ return ExpressionValue(*Result);
+}
+
+Expected<ExpressionValue> llvm::operator-(const ExpressionValue &LeftOperand,
+ const ExpressionValue &RightOperand) {
+ // Result will be negative and thus might underflow.
+ if (LeftOperand.isNegative() && !RightOperand.isNegative()) {
+ int64_t LeftValue = cantFail(LeftOperand.getSignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ // Result <= -1 - (max int64_t) which overflows on 1- and 2-complement.
+ if (RightValue > std::numeric_limits<int64_t>::max())
+ return make_error<OverflowError>();
+ Optional<int64_t> Result =
+ checkedSub(LeftValue, static_cast<int64_t>(RightValue));
+ if (!Result)
+ return make_error<OverflowError>();
+
+ return ExpressionValue(*Result);
+ }
+
+ // (-A) - (-B) == B - A.
+ if (LeftOperand.isNegative())
+ return RightOperand.getAbsolute() - LeftOperand.getAbsolute();
+
+ // A - (-B) == A + B.
+ if (RightOperand.isNegative())
+ return LeftOperand + RightOperand.getAbsolute();
+
+ // Both values are positive at this point.
+ uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue());
+ uint64_t RightValue = cantFail(RightOperand.getUnsignedValue());
+ if (LeftValue >= RightValue)
+ return ExpressionValue(LeftValue - RightValue);
+ else {
+ uint64_t AbsoluteDifference = RightValue - LeftValue;
+ uint64_t MaxInt64 = std::numeric_limits<int64_t>::max();
+ // Value might underflow.
+ if (AbsoluteDifference > MaxInt64) {
+ AbsoluteDifference -= MaxInt64;
+ int64_t Result = -MaxInt64;
+ int64_t MinInt64 = std::numeric_limits<int64_t>::min();
+ // Underflow, tested by:
+ // abs(Result + (max int64_t)) > abs((min int64_t) + (max int64_t))
+ if (AbsoluteDifference > static_cast<uint64_t>(-(MinInt64 - Result)))
+ return make_error<OverflowError>();
+ Result -= static_cast<int64_t>(AbsoluteDifference);
+ return ExpressionValue(Result);
+ }
+
+ return ExpressionValue(-static_cast<int64_t>(AbsoluteDifference));
+ }
+}
+
+Expected<ExpressionValue> NumericVariableUse::eval() const {
+ Optional<ExpressionValue> Value = Variable->getValue();
if (Value)
return *Value;
return make_error<UndefVarError>(getExpressionStr());
}
-Expected<uint64_t> BinaryOperation::eval() const {
- Expected<uint64_t> LeftOp = LeftOperand->eval();
- Expected<uint64_t> RightOp = RightOperand->eval();
+Expected<ExpressionValue> BinaryOperation::eval() const {
+ Expected<ExpressionValue> LeftOp = LeftOperand->eval();
+ Expected<ExpressionValue> RightOp = RightOperand->eval();
// Bubble up any error (e.g. undefined variables) in the recursive
// evaluation.
@@ -136,7 +286,8 @@ BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {
Expected<std::string> NumericSubstitution::getResult() const {
assert(ExpressionPointer->getAST() != nullptr &&
"Substituting empty expression");
- Expected<uint64_t> EvaluatedValue = ExpressionPointer->getAST()->eval();
+ Expected<ExpressionValue> EvaluatedValue =
+ ExpressionPointer->getAST()->eval();
if (!EvaluatedValue)
return EvaluatedValue.takeError();
ExpressionFormat Format = ExpressionPointer->getFormat();
@@ -192,6 +343,7 @@ static char popFront(StringRef &S) {
return C;
}
+char OverflowError::ID = 0;
char UndefVarError::ID = 0;
char ErrorDiagnostic::ID = 0;
char NotFoundError::ID = 0;
@@ -295,13 +447,18 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
}
// Otherwise, parse it as a literal.
- uint64_t LiteralValue;
- StringRef OperandExpr = Expr;
+ int64_t SignedLiteralValue;
+ uint64_t UnsignedLiteralValue;
+ StringRef SaveExpr = Expr;
+ // Accept both signed and unsigned literal, default to signed literal.
if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
- LiteralValue)) {
- return std::make_unique<ExpressionLiteral>(
- OperandExpr.drop_back(Expr.size()), LiteralValue);
- }
+ UnsignedLiteralValue))
+ return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
+ UnsignedLiteralValue);
+ Expr = SaveExpr;
+ if (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue))
+ return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()),
+ SignedLiteralValue);
return ErrorDiagnostic::get(SM, Expr,
"invalid operand format '" + Expr + "'");
@@ -339,14 +496,6 @@ Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber,
return SubExprResult;
}
-static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
- return LeftOp + RightOp;
-}
-
-static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
- return LeftOp - RightOp;
-}
-
Expected<std::unique_ptr<ExpressionAST>>
Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
std::unique_ptr<ExpressionAST> LeftOp,
@@ -363,10 +512,10 @@ Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
binop_eval_t EvalBinop;
switch (Operator) {
case '+':
- EvalBinop = add;
+ EvalBinop = operator+;
break;
case '-':
- EvalBinop = sub;
+ EvalBinop = operator-;
break;
default:
return ErrorDiagnostic::get(
@@ -415,6 +564,9 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
case 'u':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
break;
+ case 'd':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed);
+ break;
case 'x':
ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower);
break;
@@ -819,7 +971,7 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
if (!Substitutions.empty()) {
TmpStr = RegExStr;
if (LineNumber)
- Context->LineVariable->setValue(*LineNumber);
+ Context->LineVariable->setValue(ExpressionValue(*LineNumber));
size_t InsertOffset = 0;
// Substitute all string variables and expressions whose values are only
@@ -828,8 +980,18 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
for (const auto &Substitution : Substitutions) {
// Substitute and check for failure (e.g. use of undefined variable).
Expected<std::string> Value = Substitution->getResult();
- if (!Value)
- return Value.takeError();
+ if (!Value) {
+ // Convert to an ErrorDiagnostic to get location information. This is
+ // done here rather than PrintNoMatch since now we know which
+ // substitution block caused the overflow.
+ Error Err =
+ handleErrors(Value.takeError(), [&](const OverflowError &E) {
+ return ErrorDiagnostic::get(SM, Substitution->getFromString(),
+ "unable to substitute variable or "
+ "numeric expression: overflow error");
+ });
+ return std::move(Err);
+ }
// Plop it into the regex at the adjusted offset.
TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
@@ -870,7 +1032,8 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,
StringRef MatchedValue = MatchInfo[CaptureParenGroup];
ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
- Expected<uint64_t> Value = Format.valueFromStringRepr(MatchedValue, SM);
+ Expected<ExpressionValue> Value =
+ Format.valueFromStringRepr(MatchedValue, SM);
if (!Value)
return Value.takeError();
DefinedNumericVariable->setValue(*Value);
@@ -914,17 +1077,20 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
// variables it uses.
if (!MatchedValue) {
bool UndefSeen = false;
- handleAllErrors(MatchedValue.takeError(), [](const NotFoundError &E) {},
- // Handled in PrintNoMatch().
- [](const ErrorDiagnostic &E) {},
- [&](const UndefVarError &E) {
- if (!UndefSeen) {
- OS << "uses undefined variable(s):";
- UndefSeen = true;
- }
- OS << " ";
- E.log(OS);
- });
+ handleAllErrors(
+ MatchedValue.takeError(), [](const NotFoundError &E) {},
+ // Handled in PrintNoMatch().
+ [](const ErrorDiagnostic &E) {},
+ // Handled in match().
+ [](const OverflowError &E) {},
+ [&](const UndefVarError &E) {
+ if (!UndefSeen) {
+ OS << "uses undefined variable(s):";
+ UndefSeen = true;
+ }
+ OS << " ";
+ E.log(OS);
+ });
} else {
// Substitution succeeded. Print substituted value.
OS << "with \"";
@@ -2086,7 +2252,7 @@ Error FileCheckPatternContext::defineCmdlineVariables(
// to, since the expression of a command-line variable definition should
// only use variables defined earlier on the command-line. If not, this
// is an error and we report it.
- Expected<uint64_t> Value = Expression->getAST()->eval();
+ Expected<ExpressionValue> Value = Expression->getAST()->eval();
if (!Value) {
Errs = joinErrors(std::move(Errs), Value.takeError());
continue;
diff --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h
index f4f2fc21a208..068de3da1c69 100644
--- a/llvm/lib/Support/FileCheckImpl.h
+++ b/llvm/lib/Support/FileCheckImpl.h
@@ -31,6 +31,8 @@ namespace llvm {
// Numeric substitution handling code.
//===----------------------------------------------------------------------===//
+class ExpressionValue;
+
/// Type representing the format an expression value should be textualized into
/// for matching. Used to represent both explicit format specifiers as well as
/// implicit format from using numeric variables.
@@ -41,6 +43,8 @@ struct ExpressionFormat {
NoFormat,
/// Value is an unsigned integer and should be printed as a decimal number.
Unsigned,
+ /// Value is a signed integer and should be printed as a decimal number.
+ Signed,
/// Value should be printed as an uppercase hex number.
HexUpper,
/// Value should be printed as a lowercase hex number.
@@ -80,17 +84,64 @@ struct ExpressionFormat {
Expected<StringRef> getWildcardRegex() const;
/// \returns the string representation of \p Value in the format represented
- /// by this instance, or an error if the format is NoFormat.
- Expected<std::string> getMatchingString(uint64_t Value) const;
+ /// by this instance, or an error if conversion to this format failed or the
+ /// format is NoFormat.
+ Expected<std::string> getMatchingString(ExpressionValue Value) const;
/// \returns the value corresponding to string representation \p StrVal
/// according to the matching format represented by this instance or an error
/// with diagnostic against \p SM if \p StrVal does not correspond to a valid
/// and representable value.
- Expected<uint64_t> valueFromStringRepr(StringRef StrVal,
- const SourceMgr &SM) const;
+ Expected<ExpressionValue> valueFromStringRepr(StringRef StrVal,
+ const SourceMgr &SM) const;
};
+/// Class to represent an overflow error that might result when manipulating a
+/// value.
+class OverflowError : public ErrorInfo<OverflowError> {
+public:
+ static char ID;
+
+ std::error_code convertToErrorCode() const override {
+ return std::make_error_code(std::errc::value_too_large);
+ }
+
+ void log(raw_ostream &OS) const override { OS << "overflow error"; }
+};
+
+/// Class representing a numeric value.
+class ExpressionValue {
+private:
+ uint64_t Value;
+ bool Negative;
+
+public:
+ template <class T>
+ explicit ExpressionValue(T Val) : Value(Val), Negative(Val < 0) {}
+
+ /// Returns true if value is signed and negative, false otherwise.
+ bool isNegative() const { return Negative; }
+
+ /// \returns the value as a signed integer or an error if the value is out of
+ /// range.
+ Expected<int64_t> getSignedValue() const;
+
+ /// \returns the value as an unsigned integer or an error if the value is out
+ /// of range.
+ Expected<uint64_t> getUnsignedValue() const;
+
+ /// \returns an unsigned ExpressionValue instance whose value is the absolute
+ /// value to this object's value.
+ ExpressionValue getAbsolute() const;
+};
+
+/// Performs operation and \returns its result or an error in case of failure,
+/// such as if an overflow occurs.
+Expected<ExpressionValue> operator+(const ExpressionValue &Lhs,
+ const ExpressionValue &Rhs);
+Expected<ExpressionValue> operator-(const ExpressionValue &Lhs,
+ const ExpressionValue &Rhs);
+
/// Base class representing the AST of a given expression.
class ExpressionAST {
private:
@@ -105,7 +156,7 @@ class ExpressionAST {
/// Evaluates and \returns the value of the expression represented by this
/// AST or an error if evaluation fails.
- virtual Expected<uint64_t> eval() const = 0;
+ virtual Expected<ExpressionValue> eval() const = 0;
/// \returns either the implicit format of this AST, a diagnostic against
/// \p SM if implicit formats of the AST's components conflict, or NoFormat
@@ -121,16 +172,15 @@ class ExpressionAST {
class ExpressionLiteral : public ExpressionAST {
private:
/// Actual value of the literal.
- uint64_t Value;
+ ExpressionValue Value;
public:
- /// Constructs a literal with the specified value parsed from
- /// \p ExpressionStr.
- ExpressionLiteral(StringRef ExpressionStr, uint64_t Val)
+ template <class T>
+ explicit ExpressionLiteral(StringRef ExpressionStr, T Val)
: ExpressionAST(ExpressionStr), Value(Val) {}
/// \returns the literal's value.
- Expected<uint64_t> eval() const override { return Value; }
+ Expected<ExpressionValue> eval() const override { return Value; }
};
/// Class to represent an undefined variable error, which quotes that
@@ -190,7 +240,7 @@ class NumericVariable {
ExpressionFormat ImplicitFormat;
/// Value of numeric variable, if defined, or None otherwise.
- Optional<uint64_t> Value;
+ Optional<ExpressionValue> Value;
/// Line number where this variable is defined, or None if defined before
/// input is parsed. Used to determine whether a variable is defined on the
@@ -213,10 +263,10 @@ class NumericVariable {
ExpressionFormat getImplicitFormat() const { return ImplicitFormat; }
/// \returns this variable's value.
- Optional<uint64_t> getValue() const { return Value; }
+ Optional<ExpressionValue> getValue() const { return Value; }
/// Sets value of this numeric variable to \p NewValue.
- void setValue(uint64_t NewValue) { Value = NewValue; }
+ void setValue(ExpressionValue NewValue) { Value = NewValue; }
/// Clears value of this numeric variable, regardless of whether it is
/// currently defined or not.
@@ -238,7 +288,7 @@ class NumericVariableUse : public ExpressionAST {
NumericVariableUse(StringRef Name, NumericVariable *Variable)
: ExpressionAST(Name), Variable(Variable) {}
/// \returns the value of the variable referenced by this instance.
- Expected<uint64_t> eval() const override;
+ Expected<ExpressionValue> eval() const override;
/// \returns implicit format of this numeric variable.
Expected<ExpressionFormat>
@@ -248,7 +298,8 @@ class NumericVariableUse : public ExpressionAST {
};
/// Type of functions evaluating a given binary operation.
-using binop_eval_t = uint64_t (*)(uint64_t, uint64_t);
+using binop_eval_t = Expected<ExpressionValue> (*)(const ExpressionValue &,
+ const ExpressionValue &);
/// Class representing a single binary operation in the AST of an expression.
class BinaryOperation : public ExpressionAST {
@@ -275,7 +326,7 @@ class BinaryOperation : public ExpressionAST {
/// using EvalBinop on the result of recursively evaluating the operands.
/// \returns the expression value or an error if an undefined numeric
/// variable is used in one of the operands.
- Expected<uint64_t> eval() const override;
+ Expected<ExpressionValue> eval() const override;
/// \returns the implicit format of this AST, if any, a diagnostic against
/// \p SM if the implicit formats of the AST's components conflict, or no
diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt
index 3d33e64a0a9e..d5b4db7d30ea 100644
--- a/llvm/test/FileCheck/numeric-expression.txt
+++ b/llvm/test/FileCheck/numeric-expression.txt
@@ -19,8 +19,9 @@ REDEF NO SPC // CHECK-LABEL: REDEF
; Numeric variable definition with explicit matching format.
DEF FMT // CHECK-LABEL: DEF FMT
-c // CHECK-NEXT: {{^}}[[#%x,LHEX:]]
-D // CHECK-NEXT: {{^}}[[#%X,UHEX:]]
+c // CHECK-NEXT: {{^}}[[#%x,LHEX:]]
+D // CHECK-NEXT: {{^}}[[#%X,UHEX:]]
+-30 // CHECK-NEXT: {{^}}[[#%d,SIGN:]]
; Numeric variable definition with explicit matching format with
diff erent
; spacing.
@@ -64,6 +65,10 @@ E // CHECK-NEXT: {{^}}[[#%X,UHEX+1]]
C // CHECK-NEXT: {{^}}[[#%X,UHEX-1]]
1B // CHECK-NEXT: {{^}}[[#%X,UHEX+0xe]]
1B // CHECK-NEXT: {{^}}[[#%X,UHEX+0xE]]
+-30 // CHECK-NEXT: {{^}}[[#%d,SIGN]]
+-29 // CHECK-NEXT: {{^}}[[#%d,SIGN+1]]
+-31 // CHECK-NEXT: {{^}}[[#%d,SIGN-1]]
+42 // CHECK-NEXT: {{^}}[[#%d,SIGN+72]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIa]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIb]]
11 // CHECK-NEXT: {{^}}[[#%u,UNSIc]]
@@ -104,6 +109,9 @@ E // CHECK-NEXT: {{^}}[[#UHEX+1]]
C // CHECK-NEXT: {{^}}[[#UHEX-1]]
1B // CHECK-NEXT: {{^}}[[#UHEX+0xe]]
1B // CHECK-NEXT: {{^}}[[#UHEX+0xE]]
+-30 // CHECK-NEXT: {{^}}[[#SIGN]]
+-29 // CHECK-NEXT: {{^}}[[#SIGN+1]]
+-31 // CHECK-NEXT: {{^}}[[#SIGN-1]]
; Numeric expressions using variables defined on other lines and an immediate
; interpreted as an unsigned value.
@@ -118,10 +126,16 @@ CHECK-NEXT: [[#UNSI+0x8000000000000000]]
USE CONV FMT IMPL MATCH // CHECK-LABEL: USE CONV FMT IMPL MATCH
b // CHECK-NEXT: {{^}}[[# %x, UNSI]]
B // CHECK-NEXT: {{^}}[[# %X, UNSI]]
+-1 // CHECK-NEXT: {{^}}[[# %d, UNSI-12]]
12 // CHECK-NEXT: {{^}}[[# %u, LHEX]]
C // CHECK-NEXT: {{^}}[[# %X, LHEX]]
+-2 // CHECK-NEXT: {{^}}[[# %d, LHEX-14]]
13 // CHECK-NEXT: {{^}}[[# %u, UHEX]]
d // CHECK-NEXT: {{^}}[[# %x, UHEX]]
+-5 // CHECK-NEXT: {{^}}[[# %d, UHEX-18]]
+15 // CHECK-NEXT: {{^}}[[# %u, SIGN+45]]
+f // CHECK-NEXT: {{^}}[[# %x, SIGN+45]]
+F // CHECK-NEXT: {{^}}[[# %X, SIGN+45]]
; Conflicting implicit format.
RUN: %ProtectFileCheckOutput \
@@ -329,3 +343,27 @@ REDEF-NEW-FMT-NEXT: [[#%X,UNSI:]]
REDEF-NEW-FMT-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: format
diff erent from previous variable definition
REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,UNSI:\]\]}}
REDEF-NEW-FMT-MSG-NEXT: {{^}} ^{{$}}
+
+; Numeric expression with overflow.
+RUN: not FileCheck --check-prefix OVERFLOW --input-file %s %s 2>&1 \
+RUN: | FileCheck --check-prefix OVERFLOW-MSG --strict-whitespace %s
+
+OVERFLOW
+BIGVAR=10000000000000000
+OVERFLOW-LABEL: OVERFLOW
+OVERFLOW-NEXT: BIGVAR: [[#BIGVAR:0x8000000000000000+0x8000000000000000]]
+OVERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:27: error: unable to substitute variable or numeric expression
+OVERFLOW-MSG-NEXT: {{O}}VERFLOW-NEXT: BIGVAR: {{\[\[#BIGVAR:0x8000000000000000\+0x8000000000000000\]\]}}
+OVERFLOW-MSG-NEXT: {{^}} ^{{$}}
+
+; Numeric expression with underflow.
+RUN: not FileCheck --check-prefix UNDERFLOW --input-file %s %s 2>&1 \
+RUN: | FileCheck --check-prefix UNDERFLOW-MSG --strict-whitespace %s
+
+UNDERFLOW
+TINYVAR=-10000000000000000
+UNDERFLOW-LABEL: UNDERFLOW
+UNDERFLOW-NEXT: TINYVAR: [[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000]]
+UNDERFLOW-MSG: numeric-expression.txt:[[#@LINE-1]]:29: error: unable to substitute variable or numeric expression
+UNDERFLOW-MSG-NEXT: {{U}}NDERFLOW-NEXT: TINYVAR: {{\[\[#%d,TINYVAR:-0x8000000000000000-0x8000000000000000\]\]}}
+UNDERFLOW-MSG-NEXT: {{^}} ^{{$}}
diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp
index 75b7fba8759d..54646a036f73 100644
--- a/llvm/unittests/Support/FileCheckTest.cpp
+++ b/llvm/unittests/Support/FileCheckTest.cpp
@@ -88,13 +88,16 @@ struct ExpressionFormatParameterisedFixture
bool AllowUpperHex;
};
+const uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max();
+
TEST_P(ExpressionFormatParameterisedFixture, Format) {
SourceMgr SM;
ExpressionFormat Format(Kind);
+ bool Signed = Kind == ExpressionFormat::Kind::Signed;
Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
- Regex WildcardRegex(*WildcardPattern);
+ Regex WildcardRegex((Twine("^") + *WildcardPattern).str());
ASSERT_TRUE(WildcardRegex.isValid());
// Does not match empty string.
EXPECT_FALSE(WildcardRegex.match(""));
@@ -103,6 +106,14 @@ TEST_P(ExpressionFormatParameterisedFixture, Format) {
StringRef DecimalDigits = "0123456789";
ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches));
EXPECT_EQ(Matches[0], DecimalDigits);
+ // Matches negative digits.
+ StringRef MinusFortyTwo = "-42";
+ bool MatchSuccess = WildcardRegex.match(MinusFortyTwo, &Matches);
+ if (Signed) {
+ ASSERT_TRUE(MatchSuccess);
+ EXPECT_EQ(Matches[0], MinusFortyTwo);
+ } else
+ EXPECT_FALSE(MatchSuccess);
// Check non digits or digits with wrong casing are not matched.
if (AllowHex) {
StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"};
@@ -121,42 +132,75 @@ TEST_P(ExpressionFormatParameterisedFixture, Format) {
EXPECT_FALSE(WildcardRegex.match("A"));
}
- Expected<std::string> MatchingString = Format.getMatchingString(0U);
+ Expected<std::string> MatchingString =
+ Format.getMatchingString(ExpressionValue(0u));
ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
EXPECT_EQ(*MatchingString, "0");
- MatchingString = Format.getMatchingString(9U);
+ MatchingString = Format.getMatchingString(ExpressionValue(9u));
ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
EXPECT_EQ(*MatchingString, "9");
- Expected<std::string> TenMatchingString = Format.getMatchingString(10U);
+ MatchingString = Format.getMatchingString(ExpressionValue(-5));
+ if (Signed) {
+ ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
+ EXPECT_EQ(*MatchingString, "-5");
+ } else {
+ // Error message tested in ExpressionValue unit tests.
+ EXPECT_THAT_EXPECTED(MatchingString, Failed());
+ }
+ Expected<std::string> MaxUint64MatchingString =
+ Format.getMatchingString(ExpressionValue(MaxUint64));
+ Expected<std::string> TenMatchingString =
+ Format.getMatchingString(ExpressionValue(10u));
ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded());
- Expected<std::string> FifteenMatchingString = Format.getMatchingString(15U);
+ Expected<std::string> FifteenMatchingString =
+ Format.getMatchingString(ExpressionValue(15u));
ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded());
StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString;
+ std::string MaxUint64Str;
if (AllowHex) {
if (AllowUpperHex) {
+ MaxUint64Str = "FFFFFFFFFFFFFFFF";
ExpectedTenMatchingString = "A";
ExpectedFifteenMatchingString = "F";
} else {
+ MaxUint64Str = "ffffffffffffffff";
ExpectedTenMatchingString = "a";
ExpectedFifteenMatchingString = "f";
}
} else {
+ MaxUint64Str = std::to_string(MaxUint64);
ExpectedTenMatchingString = "10";
ExpectedFifteenMatchingString = "15";
}
+ if (Signed) {
+ // Error message tested in ExpressionValue unit tests.
+ EXPECT_THAT_EXPECTED(MaxUint64MatchingString, Failed());
+ } else {
+ ASSERT_THAT_EXPECTED(MaxUint64MatchingString, Succeeded());
+ EXPECT_EQ(*MaxUint64MatchingString, MaxUint64Str);
+ }
EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString);
EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString);
StringRef BufferizedValidValueStr = bufferize(SM, "0");
- Expected<uint64_t> Val =
+ Expected<ExpressionValue> Val =
Format.valueFromStringRepr(BufferizedValidValueStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
- EXPECT_EQ(*Val, 0U);
+ EXPECT_EQ(cantFail(Val->getSignedValue()), 0);
BufferizedValidValueStr = bufferize(SM, "9");
Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
- EXPECT_EQ(*Val, 9U);
- StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr;
+ EXPECT_EQ(cantFail(Val->getSignedValue()), 9);
+ StringRef BufferizedMinusFiveStr = bufferize(SM, "-5");
+ Val = Format.valueFromStringRepr(BufferizedMinusFiveStr, SM);
+ StringRef OverflowErrorStr = "unable to represent numeric value";
+ if (Signed) {
+ ASSERT_THAT_EXPECTED(Val, Succeeded());
+ EXPECT_EQ(cantFail(Val->getSignedValue()), -5);
+ } else
+ expectDiagnosticError(OverflowErrorStr, Val.takeError());
+ StringRef BufferizedMaxUint64Str, BufferizedTenStr, BufferizedInvalidTenStr,
+ BufferizedFifteenStr;
StringRef TenStr, FifteenStr, InvalidTenStr;
if (AllowHex) {
if (AllowUpperHex) {
@@ -173,19 +217,27 @@ TEST_P(ExpressionFormatParameterisedFixture, Format) {
FifteenStr = "15";
InvalidTenStr = "A";
}
+ BufferizedMaxUint64Str = bufferize(SM, MaxUint64Str);
+ Val = Format.valueFromStringRepr(BufferizedMaxUint64Str, SM);
+ if (Signed)
+ expectDiagnosticError(OverflowErrorStr, Val.takeError());
+ else {
+ ASSERT_THAT_EXPECTED(Val, Succeeded());
+ EXPECT_EQ(cantFail(Val->getUnsignedValue()), MaxUint64);
+ }
BufferizedTenStr = bufferize(SM, TenStr);
Val = Format.valueFromStringRepr(BufferizedTenStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
- EXPECT_EQ(*Val, 10U);
+ EXPECT_EQ(cantFail(Val->getSignedValue()), 10);
BufferizedFifteenStr = bufferize(SM, FifteenStr);
Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM);
ASSERT_THAT_EXPECTED(Val, Succeeded());
- EXPECT_EQ(*Val, 15U);
+ EXPECT_EQ(cantFail(Val->getSignedValue()), 15);
// Wrong casing is not tested because valueFromStringRepr() relies on
// StringRef's getAsInteger() which does not allow to restrict casing.
BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr);
expectDiagnosticError(
- "unable to represent numeric value",
+ OverflowErrorStr,
Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError());
// Check boolean operator.
@@ -197,6 +249,8 @@ INSTANTIATE_TEST_CASE_P(
::testing::Values(
std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false,
/*AllowUpperHex=*/false),
+ std::make_tuple(ExpressionFormat::Kind::Signed, /*AllowHex=*/false,
+ /*AllowUpperHex=*/false),
std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true,
/*AllowUpperHex=*/false),
std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true,
@@ -206,8 +260,9 @@ TEST_F(FileCheckTest, NoFormatProperties) {
ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
expectError<StringError>("trying to match value with invalid format",
NoFormat.getWildcardRegex().takeError());
- expectError<StringError>("trying to match value with invalid format",
- NoFormat.getMatchingString(18).takeError());
+ expectError<StringError>(
+ "trying to match value with invalid format",
+ NoFormat.getMatchingString(ExpressionValue(18u)).takeError());
EXPECT_FALSE(bool(NoFormat));
}
@@ -238,31 +293,221 @@ TEST_F(FileCheckTest, FormatKindEqualityOperators) {
EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
}
+template <class T1, class T2>
+static Expected<ExpressionValue> doValueOperation(binop_eval_t Operation,
+ T1 LeftValue, T2 RightValue) {
+ ExpressionValue LeftOperand(LeftValue);
+ ExpressionValue RightOperand(RightValue);
+ return Operation(LeftOperand, RightOperand);
+}
+
+template <class T>
+static void expectValueEqual(ExpressionValue ActualValue, T ExpectedValue) {
+ EXPECT_EQ(ExpectedValue < 0, ActualValue.isNegative());
+ if (ExpectedValue < 0) {
+ Expected<int64_t> SignedActualValue = ActualValue.getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedActualValue, Succeeded());
+ EXPECT_EQ(*SignedActualValue, static_cast<int64_t>(ExpectedValue));
+ } else {
+ Expected<uint64_t> UnsignedActualValue = ActualValue.getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedActualValue, Succeeded());
+ EXPECT_EQ(*UnsignedActualValue, static_cast<uint64_t>(ExpectedValue));
+ }
+}
+
+template <class T1, class T2, class TR>
+static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue,
+ T2 RightValue, TR ResultValue) {
+ Expected<ExpressionValue> OperationResult =
+ doValueOperation(Operation, LeftValue, RightValue);
+ ASSERT_THAT_EXPECTED(OperationResult, Succeeded());
+ expectValueEqual(*OperationResult, ResultValue);
+}
+
+template <class T1, class T2>
+static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue,
+ T2 RightValue) {
+ expectError<OverflowError>(
+ "overflow error",
+ doValueOperation(Operation, LeftValue, RightValue).takeError());
+}
+
+const int64_t MinInt64 = std::numeric_limits<int64_t>::min();
+const int64_t MaxInt64 = std::numeric_limits<int64_t>::max();
+
+TEST_F(FileCheckTest, ExpressionValueGetUnsigned) {
+ // Test positive value.
+ Expected<uint64_t> UnsignedValue = ExpressionValue(10).getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());
+ EXPECT_EQ(*UnsignedValue, 10U);
+
+ // Test 0.
+ UnsignedValue = ExpressionValue(0).getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());
+ EXPECT_EQ(*UnsignedValue, 0U);
+
+ // Test max positive value.
+ UnsignedValue = ExpressionValue(MaxUint64).getUnsignedValue();
+ ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded());
+ EXPECT_EQ(*UnsignedValue, MaxUint64);
+
+ // Test failure with negative value.
+ expectError<OverflowError>(
+ "overflow error", ExpressionValue(-1).getUnsignedValue().takeError());
+
+ // Test failure with min negative value.
+ expectError<OverflowError>(
+ "overflow error",
+ ExpressionValue(MinInt64).getUnsignedValue().takeError());
+}
+
+TEST_F(FileCheckTest, ExpressionValueGetSigned) {
+ // Test positive value.
+ Expected<int64_t> SignedValue = ExpressionValue(10).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, 10);
+
+ // Test 0.
+ SignedValue = ExpressionValue(0).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, 0);
+
+ // Test max int64_t.
+ SignedValue = ExpressionValue(MaxInt64).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, MaxInt64);
+
+ // Test failure with too big positive value.
+ expectError<OverflowError>(
+ "overflow error", ExpressionValue(static_cast<uint64_t>(MaxInt64) + 1)
+ .getSignedValue()
+ .takeError());
+
+ // Test failure with max uint64_t.
+ expectError<OverflowError>(
+ "overflow error",
+ ExpressionValue(MaxUint64).getSignedValue().takeError());
+
+ // Test negative value.
+ SignedValue = ExpressionValue(-10).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, -10);
+
+ // Test min int64_t.
+ SignedValue = ExpressionValue(MinInt64).getSignedValue();
+ ASSERT_THAT_EXPECTED(SignedValue, Succeeded());
+ EXPECT_EQ(*SignedValue, MinInt64);
+}
+
+TEST_F(FileCheckTest, ExpressionValueAbsolute) {
+ // Test positive value.
+ expectValueEqual(ExpressionValue(10).getAbsolute(), 10);
+
+ // Test 0.
+ expectValueEqual(ExpressionValue(0).getAbsolute(), 0);
+
+ // Test max uint64_t.
+ expectValueEqual(ExpressionValue(MaxUint64).getAbsolute(), MaxUint64);
+
+ // Test negative value.
+ expectValueEqual(ExpressionValue(-10).getAbsolute(), 10);
+
+ // Test absence of overflow on min int64_t.
+ expectValueEqual(ExpressionValue(MinInt64).getAbsolute(),
+ static_cast<uint64_t>(-(MinInt64 + 10)) + 10);
+}
+
+TEST_F(FileCheckTest, ExpressionValueAddition) {
+ // Test both negative values.
+ expectOperationValueResult(operator+, -10, -10, -20);
+
+ // Test both negative values with underflow.
+ expectOperationValueResult(operator+, MinInt64, -1);
+ expectOperationValueResult(operator+, MinInt64, MinInt64);
+
+ // Test negative and positive value.
+ expectOperationValueResult(operator+, -10, 10, 0);
+ expectOperationValueResult(operator+, -10, 11, 1);
+ expectOperationValueResult(operator+, -11, 10, -1);
+
+ // Test positive and negative value.
+ expectOperationValueResult(operator+, 10, -10, 0);
+ expectOperationValueResult(operator+, 10, -11, -1);
+ expectOperationValueResult(operator+, 11, -10, 1);
+
+ // Test both positive values.
+ expectOperationValueResult(operator+, 10, 10, 20);
+
+ // Test both positive values with overflow.
+ expectOperationValueResult(operator+, MaxUint64, 1);
+ expectOperationValueResult(operator+, MaxUint64, MaxUint64);
+}
+
+TEST_F(FileCheckTest, ExpressionValueSubtraction) {
+ // Test negative value and value bigger than int64_t max.
+ expectOperationValueResult(operator-, -10, MaxUint64);
+
+ // Test negative and positive value with underflow.
+ expectOperationValueResult(operator-, MinInt64, 1);
+
+ // Test negative and positive value.
+ expectOperationValueResult(operator-, -10, 10, -20);
+
+ // Test both negative values.
+ expectOperationValueResult(operator-, -10, -10, 0);
+ expectOperationValueResult(operator-, -11, -10, -1);
+ expectOperationValueResult(operator-, -10, -11, 1);
+
+ // Test positive and negative values.
+ expectOperationValueResult(operator-, 10, -10, 20);
+
+ // Test both positive values with result positive.
+ expectOperationValueResult(operator-, 10, 5, 5);
+
+ // Test both positive values with underflow.
+ expectOperationValueResult(operator-, 0, MaxUint64);
+ expectOperationValueResult(operator-, 0,
+ static_cast<uint64_t>(-(MinInt64 + 10)) + 11);
+
+ // Test both positive values with result < -(max int64_t)
+ expectOperationValueResult(operator-, 10,
+ static_cast<uint64_t>(MaxInt64) + 11,
+ -MaxInt64 - 1);
+
+ // Test both positive values with 0 > result > -(max int64_t)
+ expectOperationValueResult(operator-, 10, 11, -1);
+}
+
TEST_F(FileCheckTest, Literal) {
SourceMgr SM;
// Eval returns the literal's value.
- ExpressionLiteral Ten(bufferize(SM, "10"), 10);
- Expected<uint64_t> Value = Ten.eval();
+ ExpressionLiteral Ten(bufferize(SM, "10"), 10u);
+ Expected<ExpressionValue> Value = Ten.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
- EXPECT_EQ(10U, *Value);
+ EXPECT_EQ(10, cantFail(Value->getSignedValue()));
Expected<ExpressionFormat> ImplicitFormat = Ten.getImplicitFormat(SM);
ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::NoFormat);
+ // Min value can be correctly represented.
+ ExpressionLiteral Min(bufferize(SM, std::to_string(MinInt64)), MinInt64);
+ Value = Min.eval();
+ ASSERT_TRUE(bool(Value));
+ EXPECT_EQ(MinInt64, cantFail(Value->getSignedValue()));
+
// Max value can be correctly represented.
- uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max();
ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64);
Value = Max.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
- EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value);
+ EXPECT_EQ(MaxUint64, cantFail(Value->getUnsignedValue()));
}
TEST_F(FileCheckTest, Expression) {
SourceMgr SM;
std::unique_ptr<ExpressionLiteral> Ten =
- std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10);
+ std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10u);
ExpressionLiteral *TenPtr = Ten.get();
Expression Expr(std::move(Ten),
ExpressionFormat(ExpressionFormat::Kind::HexLower));
@@ -283,8 +528,6 @@ expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
}
-uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
-
TEST_F(FileCheckTest, NumericVariable) {
SourceMgr SM;
@@ -299,18 +542,18 @@ TEST_F(FileCheckTest, NumericVariable) {
ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
EXPECT_FALSE(FooVar.getValue());
- Expected<uint64_t> EvalResult = FooVarUse.eval();
+ Expected<ExpressionValue> EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
- FooVar.setValue(42);
+ FooVar.setValue(ExpressionValue(42u));
// Defined variable: getValue and eval return value set.
- Optional<uint64_t> Value = FooVar.getValue();
+ Optional<ExpressionValue> Value = FooVar.getValue();
ASSERT_TRUE(Value);
- EXPECT_EQ(42U, *Value);
+ EXPECT_EQ(42, cantFail(Value->getSignedValue()));
EvalResult = FooVarUse.eval();
ASSERT_THAT_EXPECTED(EvalResult, Succeeded());
- EXPECT_EQ(42U, *EvalResult);
+ EXPECT_EQ(42, cantFail(EvalResult->getSignedValue()));
// Clearing variable: getValue and eval fail. Error returned by eval holds
// the name of the cleared variable.
@@ -327,23 +570,24 @@ TEST_F(FileCheckTest, Binop) {
StringRef FooStr = ExprStr.take_front(3);
NumericVariable FooVar(FooStr,
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
- FooVar.setValue(42);
+ FooVar.setValue(ExpressionValue(42u));
std::unique_ptr<NumericVariableUse> FooVarUse =
std::make_unique<NumericVariableUse>(FooStr, &FooVar);
StringRef BarStr = ExprStr.take_back(3);
NumericVariable BarVar(BarStr,
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
- BarVar.setValue(18);
+ BarVar.setValue(ExpressionValue(18u));
std::unique_ptr<NumericVariableUse> BarVarUse =
std::make_unique<NumericVariableUse>(BarStr, &BarVar);
+ binop_eval_t doAdd = operator+;
BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse),
std::move(BarVarUse));
// Defined variables: eval returns right value; implicit format is as
// expected.
- Expected<uint64_t> Value = Binop.eval();
+ Expected<ExpressionValue> Value = Binop.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
- EXPECT_EQ(60U, *Value);
+ EXPECT_EQ(60, cantFail(Value->getSignedValue()));
Expected<ExpressionFormat> ImplicitFormat = Binop.getImplicitFormat(SM);
ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
@@ -366,7 +610,7 @@ TEST_F(FileCheckTest, Binop) {
StringRef EighteenStr = ExprStr.take_back(2);
FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
std::unique_ptr<ExpressionLiteral> Eighteen =
- std::make_unique<ExpressionLiteral>(EighteenStr, 18);
+ std::make_unique<ExpressionLiteral>(EighteenStr, 18u);
Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse),
std::move(Eighteen));
ImplicitFormat = Binop.getImplicitFormat(SM);
@@ -376,7 +620,7 @@ TEST_F(FileCheckTest, Binop) {
FooStr = ExprStr.take_back(3);
EighteenStr = ExprStr.take_front(2);
FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
- Eighteen = std::make_unique<ExpressionLiteral>(EighteenStr, 18);
+ Eighteen = std::make_unique<ExpressionLiteral>(EighteenStr, 18u);
Binop = BinaryOperation(ExprStr, doAdd, std::move(Eighteen),
std::move(FooVarUse));
ImplicitFormat = Binop.getImplicitFormat(SM);
@@ -655,6 +899,13 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Valid single operand expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MaxUint64)),
+ Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("0x12"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("-30"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)),
+ Succeeded());
// Invalid format.
expectDiagnosticError("invalid matching format specification in expression",
@@ -697,6 +948,7 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
// Valid expression with format specifier.
EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%d, FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());
@@ -804,7 +1056,14 @@ TEST_F(FileCheckTest, ParsePattern) {
TEST_F(FileCheckTest, Match) {
PatternTester Tester;
+ // Check a substitution error is diagnosed.
+ ASSERT_FALSE(Tester.parsePattern("[[#%u, -1]]"));
+ expectDiagnosticError(
+ "unable to substitute variable or numeric expression: overflow error",
+ Tester.match("").takeError());
+
// Check matching an empty expression only matches a number.
+ Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#]]"));
expectNotFoundError(Tester.match("FAIL").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
@@ -946,7 +1205,7 @@ TEST_F(FileCheckTest, Substitution) {
// substituted for the variable's value.
NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
1);
- NVar.setValue(10);
+ NVar.setValue(ExpressionValue(10u));
auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);
auto ExpressionN = std::make_unique<Expression>(
std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));
@@ -1056,24 +1315,24 @@ TEST_F(FileCheckTest, FileCheckContext) {
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
- Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ Expected<ExpressionValue> ExpressionVal =
+ (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
- EXPECT_EQ(*ExpressionVal, 18U);
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 18);
ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
- EXPECT_EQ(*ExpressionVal, 20U);
- ExpressionPointer =
- P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable,
- /*IsLegacyLineExpr=*/false,
- LineNumber, &Cxt, SM);
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 20);
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
+ LocalNumVar3Ref, DefinedNumericVariable,
+ /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
- EXPECT_EQ(*ExpressionVal, 12U);
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 12);
ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());
EXPECT_EQ(*EmptyVar, "");
expectUndefErrors({std::string(UnknownVarStr)}, UnknownVar.takeError());
@@ -1123,7 +1382,7 @@ TEST_F(FileCheckTest, FileCheckContext) {
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
- EXPECT_EQ(*ExpressionVal, 36U);
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36);
// Clear local variables and check global variables remain defined.
Cxt.clearLocalVars();
@@ -1135,6 +1394,6 @@ TEST_F(FileCheckTest, FileCheckContext) {
ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
- EXPECT_EQ(*ExpressionVal, 36U);
+ EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36);
}
} // namespace
More information about the llvm-commits
mailing list