1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-18 18:42:46 +02:00

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
This commit is contained in:
Thomas Preud'homme 2019-03-05 23:20:29 +00:00
parent c219487139
commit 959d70cd6e
5 changed files with 632 additions and 117 deletions

View File

@ -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:

View File

@ -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 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");
bool ValueIsSigned = Value == Kind::Signed;
StringRef OverflowErrorStr = "unable to represent numeric value";
if (ValueIsSigned) {
int64_t SignedValue;
return IntegerValue;
if (StrVal.getAsInteger(10, SignedValue))
return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
return ExpressionValue(SignedValue);
}
bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
uint64_t UnsignedValue;
if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue))
return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr);
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;

View File

@ -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 @@ public:
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 @@ public:
/// 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 @@ public:
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 @@ private:
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 @@ public:
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 @@ public:
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 @@ public:
};
/// 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 @@ public:
/// 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

View File

@ -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 different
; 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 different 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: {{^}} ^{{$}}

View File

@ -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