mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01: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:
parent
c219487139
commit
959d70cd6e
@ -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:
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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: {{^}} ^{{$}}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user