diff options
author | Thomas Preud'homme <thomasp@graphcore.ai> | 2019-03-05 23:20:29 +0000 |
---|---|---|
committer | Thomas Preud'homme <thomasp@graphcore.ai> | 2020-05-28 10:44:21 +0100 |
commit | 23ac16cf9bd4cc0bb434efcf6385baf083a2ff7b (patch) | |
tree | 46818cd084b23be7667f6cd43350d0932ef4e563 /llvm | |
parent | e533a176b3d4d936a4870cd1a3273941ba699882 (diff) | |
download | llvm-23ac16cf9bd4cc0bb434efcf6385baf083a2ff7b.zip llvm-23ac16cf9bd4cc0bb434efcf6385baf083a2ff7b.tar.gz llvm-23ac16cf9bd4cc0bb434efcf6385baf083a2ff7b.tar.bz2 |
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
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/docs/CommandGuide/FileCheck.rst | 13 | ||||
-rw-r--r-- | llvm/lib/Support/FileCheck.cpp | 262 | ||||
-rw-r--r-- | llvm/lib/Support/FileCheckImpl.h | 83 | ||||
-rw-r--r-- | llvm/test/FileCheck/numeric-expression.txt | 42 | ||||
-rw-r--r-- | llvm/unittests/Support/FileCheckTest.cpp | 347 |
5 files changed, 631 insertions, 116 deletions
diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst index d8a2e34..0512133 100644 --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -660,8 +660,8 @@ The syntax to define a numeric variable is ``[[#%<fmtspec>,<NUMVAR>:]]`` where: * ``%<fmtspec>`` is an optional scanf-style matching format specifier to indicate what number format to match (e.g. hex number). Currently accepted - format specifiers are ``%u``, ``%x`` and ``%X``. If absent, the format - specifier defaults to ``%u``. + format specifiers are ``%u``, ``%d``, ``%x`` and ``%X``. If absent, the + format specifier defaults to ``%u``. * ``<NUMVAR>`` is the name of the numeric variable to define to the matching value. @@ -692,10 +692,11 @@ The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where: * an expression followed by an operator and a numeric operand. A numeric operand is a previously defined numeric variable, or an integer - literal. The supported operators are ``+`` and ``-``. Spaces are accepted - before, after and between any of these elements. - There is currently no support for operator precendence, but parentheses can - be used to change the evaluation order. + literal and have a 64-bit precision. The supported operators are ``+`` and + ``-``. Spaces are accepted before, after and between any of these elements. + Overflow and underflow are rejected. There is currently no support for + operator precendence, but parentheses can be used to change the evaluation + order. For example: diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp index 300eea8..454f3813 100644 --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -17,6 +17,7 @@ #include "FileCheckImpl.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/CheckedArithmetic.h" #include "llvm/Support/FormatVariadic.h" #include <cstdint> #include <list> @@ -31,6 +32,8 @@ StringRef ExpressionFormat::toString() const { return StringRef("<none>"); case Kind::Unsigned: return StringRef("%u"); + case Kind::Signed: + return StringRef("%d"); case Kind::HexUpper: return StringRef("%X"); case Kind::HexLower: @@ -43,6 +46,8 @@ Expected<StringRef> ExpressionFormat::getWildcardRegex() const { switch (Value) { case Kind::Unsigned: return StringRef("[0-9]+"); + case Kind::Signed: + return StringRef("-?[0-9]+"); case Kind::HexUpper: return StringRef("[0-9A-F]+"); case Kind::HexLower: @@ -54,43 +59,188 @@ Expected<StringRef> ExpressionFormat::getWildcardRegex() const { } Expected<std::string> -ExpressionFormat::getMatchingString(uint64_t IntegerValue) const { +ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const { + if (Value == Kind::Signed) { + Expected<int64_t> SignedValue = IntegerValue.getSignedValue(); + if (!SignedValue) + return SignedValue.takeError(); + return itostr(*SignedValue); + } + + Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue(); + if (!UnsignedValue) + return UnsignedValue.takeError(); switch (Value) { case Kind::Unsigned: - return utostr(IntegerValue); + return utostr(*UnsignedValue); case Kind::HexUpper: - return utohexstr(IntegerValue, /*LowerCase=*/false); + return utohexstr(*UnsignedValue, /*LowerCase=*/false); case Kind::HexLower: - return utohexstr(IntegerValue, /*LowerCase=*/true); + return utohexstr(*UnsignedValue, /*LowerCase=*/true); default: return createStringError(std::errc::invalid_argument, "trying to match value with invalid format"); } } -Expected<uint64_t> +Expected<ExpressionValue> ExpressionFormat::valueFromStringRepr(StringRef StrVal, const SourceMgr &SM) const { + bool ValueIsSigned = Value == Kind::Signed; + StringRef OverflowErrorStr = "unable to represent numeric value"; + if (ValueIsSigned) { + int64_t SignedValue; + + if (StrVal.getAsInteger(10, SignedValue)) + return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); + + return ExpressionValue(SignedValue); + } + bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; - uint64_t IntegerValue; - if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue)) - return ErrorDiagnostic::get(SM, StrVal, - "unable to represent numeric value"); + uint64_t UnsignedValue; + if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue)) + return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); - return IntegerValue; + return ExpressionValue(UnsignedValue); } -Expected<uint64_t> NumericVariableUse::eval() const { - Optional<uint64_t> Value = Variable->getValue(); +static int64_t getAsSigned(uint64_t UnsignedValue) { + // Use memcpy to reinterpret the bitpattern in Value since casting to + // signed is implementation-defined if the unsigned value is too big to be + // represented in the signed type and using an union violates type aliasing + // rules. + int64_t SignedValue; + memcpy(&SignedValue, &UnsignedValue, sizeof(SignedValue)); + return SignedValue; +} + +Expected<int64_t> ExpressionValue::getSignedValue() const { + if (Negative) + return getAsSigned(Value); + + if (Value > std::numeric_limits<int64_t>::max()) + return make_error<OverflowError>(); + + // Value is in the representable range of int64_t so we can use cast. + return static_cast<int64_t>(Value); +} + +Expected<uint64_t> ExpressionValue::getUnsignedValue() const { + if (Negative) + return make_error<OverflowError>(); + + return Value; +} + +ExpressionValue ExpressionValue::getAbsolute() const { + if (!Negative) + return *this; + + int64_t SignedValue = getAsSigned(Value); + int64_t MaxInt64 = std::numeric_limits<int64_t>::max(); + // Absolute value can be represented as int64_t. + if (SignedValue >= -MaxInt64) + return ExpressionValue(-getAsSigned(Value)); + + // -X == -(max int64_t + Rem), negate each component independently. + SignedValue += MaxInt64; + uint64_t RemainingValueAbsolute = -SignedValue; + return ExpressionValue(MaxInt64 + RemainingValueAbsolute); +} + +Expected<ExpressionValue> llvm::operator+(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + if (LeftOperand.isNegative() && RightOperand.isNegative()) { + int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); + int64_t RightValue = cantFail(RightOperand.getSignedValue()); + Optional<int64_t> Result = checkedAdd<int64_t>(LeftValue, RightValue); + if (!Result) + return make_error<OverflowError>(); + + return ExpressionValue(*Result); + } + + // (-A) + B == B - A. + if (LeftOperand.isNegative()) + return RightOperand - LeftOperand.getAbsolute(); + + // A + (-B) == A - B. + if (RightOperand.isNegative()) + return LeftOperand - RightOperand.getAbsolute(); + + // Both values are positive at this point. + uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + Optional<uint64_t> Result = + checkedAddUnsigned<uint64_t>(LeftValue, RightValue); + if (!Result) + return make_error<OverflowError>(); + + return ExpressionValue(*Result); +} + +Expected<ExpressionValue> llvm::operator-(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + // Result will be negative and thus might underflow. + if (LeftOperand.isNegative() && !RightOperand.isNegative()) { + int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + // Result <= -1 - (max int64_t) which overflows on 1- and 2-complement. + if (RightValue > std::numeric_limits<int64_t>::max()) + return make_error<OverflowError>(); + Optional<int64_t> Result = + checkedSub(LeftValue, static_cast<int64_t>(RightValue)); + if (!Result) + return make_error<OverflowError>(); + + return ExpressionValue(*Result); + } + + // (-A) - (-B) == B - A. + if (LeftOperand.isNegative()) + return RightOperand.getAbsolute() - LeftOperand.getAbsolute(); + + // A - (-B) == A + B. + if (RightOperand.isNegative()) + return LeftOperand + RightOperand.getAbsolute(); + + // Both values are positive at this point. + uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + if (LeftValue >= RightValue) + return ExpressionValue(LeftValue - RightValue); + else { + uint64_t AbsoluteDifference = RightValue - LeftValue; + uint64_t MaxInt64 = std::numeric_limits<int64_t>::max(); + // Value might underflow. + if (AbsoluteDifference > MaxInt64) { + AbsoluteDifference -= MaxInt64; + int64_t Result = -MaxInt64; + int64_t MinInt64 = std::numeric_limits<int64_t>::min(); + // Underflow, tested by: + // abs(Result + (max int64_t)) > abs((min int64_t) + (max int64_t)) + if (AbsoluteDifference > static_cast<uint64_t>(-(MinInt64 - Result))) + return make_error<OverflowError>(); + Result -= static_cast<int64_t>(AbsoluteDifference); + return ExpressionValue(Result); + } + + return ExpressionValue(-static_cast<int64_t>(AbsoluteDifference)); + } +} + +Expected<ExpressionValue> NumericVariableUse::eval() const { + Optional<ExpressionValue> Value = Variable->getValue(); if (Value) return *Value; return make_error<UndefVarError>(getExpressionStr()); } -Expected<uint64_t> BinaryOperation::eval() const { - Expected<uint64_t> LeftOp = LeftOperand->eval(); - Expected<uint64_t> RightOp = RightOperand->eval(); +Expected<ExpressionValue> BinaryOperation::eval() const { + Expected<ExpressionValue> LeftOp = LeftOperand->eval(); + Expected<ExpressionValue> RightOp = RightOperand->eval(); // Bubble up any error (e.g. undefined variables) in the recursive // evaluation. @@ -136,7 +286,8 @@ BinaryOperation::getImplicitFormat(const SourceMgr &SM) const { Expected<std::string> NumericSubstitution::getResult() const { assert(ExpressionPointer->getAST() != nullptr && "Substituting empty expression"); - Expected<uint64_t> EvaluatedValue = ExpressionPointer->getAST()->eval(); + Expected<ExpressionValue> EvaluatedValue = + ExpressionPointer->getAST()->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); ExpressionFormat Format = ExpressionPointer->getFormat(); @@ -192,6 +343,7 @@ static char popFront(StringRef &S) { return C; } +char OverflowError::ID = 0; char UndefVarError::ID = 0; char ErrorDiagnostic::ID = 0; char NotFoundError::ID = 0; @@ -295,13 +447,18 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand( } // Otherwise, parse it as a literal. - uint64_t LiteralValue; - StringRef OperandExpr = Expr; + int64_t SignedLiteralValue; + uint64_t UnsignedLiteralValue; + StringRef SaveExpr = Expr; + // Accept both signed and unsigned literal, default to signed literal. if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, - LiteralValue)) { - return std::make_unique<ExpressionLiteral>( - OperandExpr.drop_back(Expr.size()), LiteralValue); - } + UnsignedLiteralValue)) + return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()), + UnsignedLiteralValue); + Expr = SaveExpr; + if (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue)) + return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()), + SignedLiteralValue); return ErrorDiagnostic::get(SM, Expr, "invalid operand format '" + Expr + "'"); @@ -339,14 +496,6 @@ Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber, return SubExprResult; } -static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp + RightOp; -} - -static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp - RightOp; -} - Expected<std::unique_ptr<ExpressionAST>> Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, std::unique_ptr<ExpressionAST> LeftOp, @@ -363,10 +512,10 @@ Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, binop_eval_t EvalBinop; switch (Operator) { case '+': - EvalBinop = add; + EvalBinop = operator+; break; case '-': - EvalBinop = sub; + EvalBinop = operator-; break; default: return ErrorDiagnostic::get( @@ -415,6 +564,9 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock( case 'u': ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned); break; + case 'd': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed); + break; case 'x': ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower); break; @@ -819,7 +971,7 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen, if (!Substitutions.empty()) { TmpStr = RegExStr; if (LineNumber) - Context->LineVariable->setValue(*LineNumber); + Context->LineVariable->setValue(ExpressionValue(*LineNumber)); size_t InsertOffset = 0; // Substitute all string variables and expressions whose values are only @@ -828,8 +980,18 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen, for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). Expected<std::string> Value = Substitution->getResult(); - if (!Value) - return Value.takeError(); + if (!Value) { + // Convert to an ErrorDiagnostic to get location information. This is + // done here rather than PrintNoMatch since now we know which + // substitution block caused the overflow. + Error Err = + handleErrors(Value.takeError(), [&](const OverflowError &E) { + return ErrorDiagnostic::get(SM, Substitution->getFromString(), + "unable to substitute variable or " + "numeric expression: overflow error"); + }); + return std::move(Err); + } // Plop it into the regex at the adjusted offset. TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset, @@ -870,7 +1032,8 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen, StringRef MatchedValue = MatchInfo[CaptureParenGroup]; ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat(); - Expected<uint64_t> Value = Format.valueFromStringRepr(MatchedValue, SM); + Expected<ExpressionValue> Value = + Format.valueFromStringRepr(MatchedValue, SM); if (!Value) return Value.takeError(); DefinedNumericVariable->setValue(*Value); @@ -914,17 +1077,20 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer, // variables it uses. if (!MatchedValue) { bool UndefSeen = false; - handleAllErrors(MatchedValue.takeError(), [](const NotFoundError &E) {}, - // Handled in PrintNoMatch(). - [](const ErrorDiagnostic &E) {}, - [&](const UndefVarError &E) { - if (!UndefSeen) { - OS << "uses undefined variable(s):"; - UndefSeen = true; - } - OS << " "; - E.log(OS); - }); + handleAllErrors( + MatchedValue.takeError(), [](const NotFoundError &E) {}, + // Handled in PrintNoMatch(). + [](const ErrorDiagnostic &E) {}, + // Handled in match(). + [](const OverflowError &E) {}, + [&](const UndefVarError &E) { + if (!UndefSeen) { + OS << "uses undefined variable(s):"; + UndefSeen = true; + } + OS << " "; + E.log(OS); + }); } else { // Substitution succeeded. Print substituted value. OS << "with \""; @@ -2086,7 +2252,7 @@ Error FileCheckPatternContext::defineCmdlineVariables( // to, since the expression of a command-line variable definition should // only use variables defined earlier on the command-line. If not, this // is an error and we report it. - Expected<uint64_t> Value = Expression->getAST()->eval(); + Expected<ExpressionValue> Value = Expression->getAST()->eval(); if (!Value) { Errs = joinErrors(std::move(Errs), Value.takeError()); continue; diff --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h index f4f2fc2..068de3d 100644 --- a/llvm/lib/Support/FileCheckImpl.h +++ b/llvm/lib/Support/FileCheckImpl.h @@ -31,6 +31,8 @@ namespace llvm { // Numeric substitution handling code. //===----------------------------------------------------------------------===// +class ExpressionValue; + /// Type representing the format an expression value should be textualized into /// for matching. Used to represent both explicit format specifiers as well as /// implicit format from using numeric variables. @@ -41,6 +43,8 @@ struct ExpressionFormat { NoFormat, /// Value is an unsigned integer and should be printed as a decimal number. Unsigned, + /// Value is a signed integer and should be printed as a decimal number. + Signed, /// Value should be printed as an uppercase hex number. HexUpper, /// Value should be printed as a lowercase hex number. @@ -80,17 +84,64 @@ 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 diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt index 3d33e64..d5b4db7 100644 --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -19,8 +19,9 @@ REDEF NO SPC // CHECK-LABEL: REDEF ; Numeric variable definition with explicit matching format. DEF FMT // CHECK-LABEL: DEF FMT -c // CHECK-NEXT: {{^}}[[#%x,LHEX:]] -D // CHECK-NEXT: {{^}}[[#%X,UHEX:]] +c // CHECK-NEXT: {{^}}[[#%x,LHEX:]] +D // CHECK-NEXT: {{^}}[[#%X,UHEX:]] +-30 // CHECK-NEXT: {{^}}[[#%d,SIGN:]] ; Numeric variable definition with explicit matching format with 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: {{^}} ^{{$}} diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp index 75b7fba..54646a0 100644 --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -88,13 +88,16 @@ struct ExpressionFormatParameterisedFixture bool AllowUpperHex; }; +const uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max(); + TEST_P(ExpressionFormatParameterisedFixture, Format) { SourceMgr SM; ExpressionFormat Format(Kind); + bool Signed = Kind == ExpressionFormat::Kind::Signed; Expected<StringRef> WildcardPattern = Format.getWildcardRegex(); ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded()); - Regex WildcardRegex(*WildcardPattern); + Regex WildcardRegex((Twine("^") + *WildcardPattern).str()); ASSERT_TRUE(WildcardRegex.isValid()); // Does not match empty string. EXPECT_FALSE(WildcardRegex.match("")); @@ -103,6 +106,14 @@ TEST_P(ExpressionFormatParameterisedFixture, Format) { StringRef DecimalDigits = "0123456789"; ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches)); EXPECT_EQ(Matches[0], DecimalDigits); + // Matches negative digits. + StringRef MinusFortyTwo = "-42"; + bool MatchSuccess = WildcardRegex.match(MinusFortyTwo, &Matches); + if (Signed) { + ASSERT_TRUE(MatchSuccess); + EXPECT_EQ(Matches[0], MinusFortyTwo); + } else + EXPECT_FALSE(MatchSuccess); // Check non digits or digits with wrong casing are not matched. if (AllowHex) { StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"}; @@ -121,42 +132,75 @@ TEST_P(ExpressionFormatParameterisedFixture, Format) { EXPECT_FALSE(WildcardRegex.match("A")); } - Expected<std::string> MatchingString = Format.getMatchingString(0U); + Expected<std::string> MatchingString = + Format.getMatchingString(ExpressionValue(0u)); ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); EXPECT_EQ(*MatchingString, "0"); - MatchingString = Format.getMatchingString(9U); + MatchingString = Format.getMatchingString(ExpressionValue(9u)); ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); EXPECT_EQ(*MatchingString, "9"); - Expected<std::string> TenMatchingString = Format.getMatchingString(10U); + MatchingString = Format.getMatchingString(ExpressionValue(-5)); + if (Signed) { + ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); + EXPECT_EQ(*MatchingString, "-5"); + } else { + // Error message tested in ExpressionValue unit tests. + EXPECT_THAT_EXPECTED(MatchingString, Failed()); + } + Expected<std::string> MaxUint64MatchingString = + Format.getMatchingString(ExpressionValue(MaxUint64)); + Expected<std::string> TenMatchingString = + Format.getMatchingString(ExpressionValue(10u)); ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded()); - Expected<std::string> FifteenMatchingString = Format.getMatchingString(15U); + Expected<std::string> FifteenMatchingString = + Format.getMatchingString(ExpressionValue(15u)); ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded()); StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString; + std::string MaxUint64Str; if (AllowHex) { if (AllowUpperHex) { + MaxUint64Str = "FFFFFFFFFFFFFFFF"; ExpectedTenMatchingString = "A"; ExpectedFifteenMatchingString = "F"; } else { + MaxUint64Str = "ffffffffffffffff"; ExpectedTenMatchingString = "a"; ExpectedFifteenMatchingString = "f"; } } else { + MaxUint64Str = std::to_string(MaxUint64); ExpectedTenMatchingString = "10"; ExpectedFifteenMatchingString = "15"; } + if (Signed) { + // Error message tested in ExpressionValue unit tests. + EXPECT_THAT_EXPECTED(MaxUint64MatchingString, Failed()); + } else { + ASSERT_THAT_EXPECTED(MaxUint64MatchingString, Succeeded()); + EXPECT_EQ(*MaxUint64MatchingString, MaxUint64Str); + } EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString); EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString); StringRef BufferizedValidValueStr = bufferize(SM, "0"); - Expected<uint64_t> Val = + Expected<ExpressionValue> Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM); ASSERT_THAT_EXPECTED(Val, Succeeded()); - EXPECT_EQ(*Val, 0U); + EXPECT_EQ(cantFail(Val->getSignedValue()), 0); BufferizedValidValueStr = bufferize(SM, "9"); Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM); ASSERT_THAT_EXPECTED(Val, Succeeded()); - EXPECT_EQ(*Val, 9U); - StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr; + EXPECT_EQ(cantFail(Val->getSignedValue()), 9); + StringRef BufferizedMinusFiveStr = bufferize(SM, "-5"); + Val = Format.valueFromStringRepr(BufferizedMinusFiveStr, SM); + StringRef OverflowErrorStr = "unable to represent numeric value"; + if (Signed) { + ASSERT_THAT_EXPECTED(Val, Succeeded()); + EXPECT_EQ(cantFail(Val->getSignedValue()), -5); + } else + expectDiagnosticError(OverflowErrorStr, Val.takeError()); + StringRef BufferizedMaxUint64Str, BufferizedTenStr, BufferizedInvalidTenStr, + BufferizedFifteenStr; StringRef TenStr, FifteenStr, InvalidTenStr; if (AllowHex) { if (AllowUpperHex) { @@ -173,19 +217,27 @@ TEST_P(ExpressionFormatParameterisedFixture, Format) { FifteenStr = "15"; InvalidTenStr = "A"; } + BufferizedMaxUint64Str = bufferize(SM, MaxUint64Str); + Val = Format.valueFromStringRepr(BufferizedMaxUint64Str, SM); + if (Signed) + expectDiagnosticError(OverflowErrorStr, Val.takeError()); + else { + ASSERT_THAT_EXPECTED(Val, Succeeded()); + EXPECT_EQ(cantFail(Val->getUnsignedValue()), MaxUint64); + } BufferizedTenStr = bufferize(SM, TenStr); Val = Format.valueFromStringRepr(BufferizedTenStr, SM); ASSERT_THAT_EXPECTED(Val, Succeeded()); - EXPECT_EQ(*Val, 10U); + EXPECT_EQ(cantFail(Val->getSignedValue()), 10); BufferizedFifteenStr = bufferize(SM, FifteenStr); Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM); ASSERT_THAT_EXPECTED(Val, Succeeded()); - EXPECT_EQ(*Val, 15U); + EXPECT_EQ(cantFail(Val->getSignedValue()), 15); // Wrong casing is not tested because valueFromStringRepr() relies on // StringRef's getAsInteger() which does not allow to restrict casing. BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr); expectDiagnosticError( - "unable to represent numeric value", + OverflowErrorStr, Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError()); // Check boolean operator. @@ -197,6 +249,8 @@ INSTANTIATE_TEST_CASE_P( ::testing::Values( std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false, /*AllowUpperHex=*/false), + std::make_tuple(ExpressionFormat::Kind::Signed, /*AllowHex=*/false, + /*AllowUpperHex=*/false), std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true, /*AllowUpperHex=*/false), std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true, @@ -206,8 +260,9 @@ TEST_F(FileCheckTest, NoFormatProperties) { ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); expectError<StringError>("trying to match value with invalid format", NoFormat.getWildcardRegex().takeError()); - expectError<StringError>("trying to match value with invalid format", - NoFormat.getMatchingString(18).takeError()); + expectError<StringError>( + "trying to match value with invalid format", + NoFormat.getMatchingString(ExpressionValue(18u)).takeError()); EXPECT_FALSE(bool(NoFormat)); } @@ -238,31 +293,221 @@ TEST_F(FileCheckTest, FormatKindEqualityOperators) { EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat); } +template <class T1, class T2> +static Expected<ExpressionValue> doValueOperation(binop_eval_t Operation, + T1 LeftValue, T2 RightValue) { + ExpressionValue LeftOperand(LeftValue); + ExpressionValue RightOperand(RightValue); + return Operation(LeftOperand, RightOperand); +} + +template <class T> +static void expectValueEqual(ExpressionValue ActualValue, T ExpectedValue) { + EXPECT_EQ(ExpectedValue < 0, ActualValue.isNegative()); + if (ExpectedValue < 0) { + Expected<int64_t> SignedActualValue = ActualValue.getSignedValue(); + ASSERT_THAT_EXPECTED(SignedActualValue, Succeeded()); + EXPECT_EQ(*SignedActualValue, static_cast<int64_t>(ExpectedValue)); + } else { + Expected<uint64_t> UnsignedActualValue = ActualValue.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedActualValue, Succeeded()); + EXPECT_EQ(*UnsignedActualValue, static_cast<uint64_t>(ExpectedValue)); + } +} + +template <class T1, class T2, class TR> +static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue, + T2 RightValue, TR ResultValue) { + Expected<ExpressionValue> OperationResult = + doValueOperation(Operation, LeftValue, RightValue); + ASSERT_THAT_EXPECTED(OperationResult, Succeeded()); + expectValueEqual(*OperationResult, ResultValue); +} + +template <class T1, class T2> +static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue, + T2 RightValue) { + expectError<OverflowError>( + "overflow error", + doValueOperation(Operation, LeftValue, RightValue).takeError()); +} + +const int64_t MinInt64 = std::numeric_limits<int64_t>::min(); +const int64_t MaxInt64 = std::numeric_limits<int64_t>::max(); + +TEST_F(FileCheckTest, ExpressionValueGetUnsigned) { + // Test positive value. + Expected<uint64_t> UnsignedValue = ExpressionValue(10).getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, 10U); + + // Test 0. + UnsignedValue = ExpressionValue(0).getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, 0U); + + // Test max positive value. + UnsignedValue = ExpressionValue(MaxUint64).getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, MaxUint64); + + // Test failure with negative value. + expectError<OverflowError>( + "overflow error", ExpressionValue(-1).getUnsignedValue().takeError()); + + // Test failure with min negative value. + expectError<OverflowError>( + "overflow error", + ExpressionValue(MinInt64).getUnsignedValue().takeError()); +} + +TEST_F(FileCheckTest, ExpressionValueGetSigned) { + // Test positive value. + Expected<int64_t> SignedValue = ExpressionValue(10).getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, 10); + + // Test 0. + SignedValue = ExpressionValue(0).getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, 0); + + // Test max int64_t. + SignedValue = ExpressionValue(MaxInt64).getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, MaxInt64); + + // Test failure with too big positive value. + expectError<OverflowError>( + "overflow error", ExpressionValue(static_cast<uint64_t>(MaxInt64) + 1) + .getSignedValue() + .takeError()); + + // Test failure with max uint64_t. + expectError<OverflowError>( + "overflow error", + ExpressionValue(MaxUint64).getSignedValue().takeError()); + + // Test negative value. + SignedValue = ExpressionValue(-10).getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, -10); + + // Test min int64_t. + SignedValue = ExpressionValue(MinInt64).getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, MinInt64); +} + +TEST_F(FileCheckTest, ExpressionValueAbsolute) { + // Test positive value. + expectValueEqual(ExpressionValue(10).getAbsolute(), 10); + + // Test 0. + expectValueEqual(ExpressionValue(0).getAbsolute(), 0); + + // Test max uint64_t. + expectValueEqual(ExpressionValue(MaxUint64).getAbsolute(), MaxUint64); + + // Test negative value. + expectValueEqual(ExpressionValue(-10).getAbsolute(), 10); + + // Test absence of overflow on min int64_t. + expectValueEqual(ExpressionValue(MinInt64).getAbsolute(), + static_cast<uint64_t>(-(MinInt64 + 10)) + 10); +} + +TEST_F(FileCheckTest, ExpressionValueAddition) { + // Test both negative values. + expectOperationValueResult(operator+, -10, -10, -20); + + // Test both negative values with underflow. + expectOperationValueResult(operator+, MinInt64, -1); + expectOperationValueResult(operator+, MinInt64, MinInt64); + + // Test negative and positive value. + expectOperationValueResult(operator+, -10, 10, 0); + expectOperationValueResult(operator+, -10, 11, 1); + expectOperationValueResult(operator+, -11, 10, -1); + + // Test positive and negative value. + expectOperationValueResult(operator+, 10, -10, 0); + expectOperationValueResult(operator+, 10, -11, -1); + expectOperationValueResult(operator+, 11, -10, 1); + + // Test both positive values. + expectOperationValueResult(operator+, 10, 10, 20); + + // Test both positive values with overflow. + expectOperationValueResult(operator+, MaxUint64, 1); + expectOperationValueResult(operator+, MaxUint64, MaxUint64); +} + +TEST_F(FileCheckTest, ExpressionValueSubtraction) { + // Test negative value and value bigger than int64_t max. + expectOperationValueResult(operator-, -10, MaxUint64); + + // Test negative and positive value with underflow. + expectOperationValueResult(operator-, MinInt64, 1); + + // Test negative and positive value. + expectOperationValueResult(operator-, -10, 10, -20); + + // Test both negative values. + expectOperationValueResult(operator-, -10, -10, 0); + expectOperationValueResult(operator-, -11, -10, -1); + expectOperationValueResult(operator-, -10, -11, 1); + + // Test positive and negative values. + expectOperationValueResult(operator-, 10, -10, 20); + + // Test both positive values with result positive. + expectOperationValueResult(operator-, 10, 5, 5); + + // Test both positive values with underflow. + expectOperationValueResult(operator-, 0, MaxUint64); + expectOperationValueResult(operator-, 0, + static_cast<uint64_t>(-(MinInt64 + 10)) + 11); + + // Test both positive values with result < -(max int64_t) + expectOperationValueResult(operator-, 10, + static_cast<uint64_t>(MaxInt64) + 11, + -MaxInt64 - 1); + + // Test both positive values with 0 > result > -(max int64_t) + expectOperationValueResult(operator-, 10, 11, -1); +} + TEST_F(FileCheckTest, Literal) { SourceMgr SM; // Eval returns the literal's value. - ExpressionLiteral Ten(bufferize(SM, "10"), 10); - Expected<uint64_t> Value = Ten.eval(); + ExpressionLiteral Ten(bufferize(SM, "10"), 10u); + Expected<ExpressionValue> Value = Ten.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(10U, *Value); + EXPECT_EQ(10, cantFail(Value->getSignedValue())); Expected<ExpressionFormat> ImplicitFormat = Ten.getImplicitFormat(SM); ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::NoFormat); + // Min value can be correctly represented. + ExpressionLiteral Min(bufferize(SM, std::to_string(MinInt64)), MinInt64); + Value = Min.eval(); + ASSERT_TRUE(bool(Value)); + EXPECT_EQ(MinInt64, cantFail(Value->getSignedValue())); + // Max value can be correctly represented. - uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max(); ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64); Value = Max.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value); + EXPECT_EQ(MaxUint64, cantFail(Value->getUnsignedValue())); } TEST_F(FileCheckTest, Expression) { SourceMgr SM; std::unique_ptr<ExpressionLiteral> Ten = - std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10); + std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10u); ExpressionLiteral *TenPtr = Ten.get(); Expression Expr(std::move(Ten), ExpressionFormat(ExpressionFormat::Kind::HexLower)); @@ -283,8 +528,6 @@ expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames, EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames); } -uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } - TEST_F(FileCheckTest, NumericVariable) { SourceMgr SM; @@ -299,18 +542,18 @@ TEST_F(FileCheckTest, NumericVariable) { ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); EXPECT_FALSE(FooVar.getValue()); - Expected<uint64_t> EvalResult = FooVarUse.eval(); + Expected<ExpressionValue> EvalResult = FooVarUse.eval(); expectUndefErrors({"FOO"}, EvalResult.takeError()); - FooVar.setValue(42); + FooVar.setValue(ExpressionValue(42u)); // Defined variable: getValue and eval return value set. - Optional<uint64_t> Value = FooVar.getValue(); + Optional<ExpressionValue> Value = FooVar.getValue(); ASSERT_TRUE(Value); - EXPECT_EQ(42U, *Value); + EXPECT_EQ(42, cantFail(Value->getSignedValue())); EvalResult = FooVarUse.eval(); ASSERT_THAT_EXPECTED(EvalResult, Succeeded()); - EXPECT_EQ(42U, *EvalResult); + EXPECT_EQ(42, cantFail(EvalResult->getSignedValue())); // Clearing variable: getValue and eval fail. Error returned by eval holds // the name of the cleared variable. @@ -327,23 +570,24 @@ TEST_F(FileCheckTest, Binop) { StringRef FooStr = ExprStr.take_front(3); NumericVariable FooVar(FooStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - FooVar.setValue(42); + FooVar.setValue(ExpressionValue(42u)); std::unique_ptr<NumericVariableUse> FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar); StringRef BarStr = ExprStr.take_back(3); NumericVariable BarVar(BarStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2); - BarVar.setValue(18); + BarVar.setValue(ExpressionValue(18u)); std::unique_ptr<NumericVariableUse> BarVarUse = std::make_unique<NumericVariableUse>(BarStr, &BarVar); + binop_eval_t doAdd = operator+; BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse), std::move(BarVarUse)); // Defined variables: eval returns right value; implicit format is as // expected. - Expected<uint64_t> Value = Binop.eval(); + Expected<ExpressionValue> Value = Binop.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(60U, *Value); + EXPECT_EQ(60, cantFail(Value->getSignedValue())); Expected<ExpressionFormat> ImplicitFormat = Binop.getImplicitFormat(SM); ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); @@ -366,7 +610,7 @@ TEST_F(FileCheckTest, Binop) { StringRef EighteenStr = ExprStr.take_back(2); FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar); std::unique_ptr<ExpressionLiteral> Eighteen = - std::make_unique<ExpressionLiteral>(EighteenStr, 18); + std::make_unique<ExpressionLiteral>(EighteenStr, 18u); Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse), std::move(Eighteen)); ImplicitFormat = Binop.getImplicitFormat(SM); @@ -376,7 +620,7 @@ TEST_F(FileCheckTest, Binop) { FooStr = ExprStr.take_back(3); EighteenStr = ExprStr.take_front(2); FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar); - Eighteen = std::make_unique<ExpressionLiteral>(EighteenStr, 18); + Eighteen = std::make_unique<ExpressionLiteral>(EighteenStr, 18u); Binop = BinaryOperation(ExprStr, doAdd, std::move(Eighteen), std::move(FooVarUse)); ImplicitFormat = Binop.getImplicitFormat(SM); @@ -655,6 +899,13 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) { // Valid single operand expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MaxUint64)), + Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("0x12"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("-30"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)), + Succeeded()); // Invalid format. expectDiagnosticError("invalid matching format specification in expression", @@ -697,6 +948,7 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) { // Valid expression with format specifier. EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("%d, FOO"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded()); EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded()); @@ -804,7 +1056,14 @@ TEST_F(FileCheckTest, ParsePattern) { TEST_F(FileCheckTest, Match) { PatternTester Tester; + // Check a substitution error is diagnosed. + ASSERT_FALSE(Tester.parsePattern("[[#%u, -1]]")); + expectDiagnosticError( + "unable to substitute variable or numeric expression: overflow error", + Tester.match("").takeError()); + // Check matching an empty expression only matches a number. + Tester.initNextPattern(); ASSERT_FALSE(Tester.parsePattern("[[#]]")); expectNotFoundError(Tester.match("FAIL").takeError()); EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded()); @@ -946,7 +1205,7 @@ TEST_F(FileCheckTest, Substitution) { // substituted for the variable's value. NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - NVar.setValue(10); + NVar.setValue(ExpressionValue(10u)); auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar); auto ExpressionN = std::make_unique<Expression>( std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper)); @@ -1056,24 +1315,24 @@ TEST_F(FileCheckTest, FileCheckContext) { Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); - Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval(); + Expected<ExpressionValue> ExpressionVal = + (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); - EXPECT_EQ(*ExpressionVal, 18U); + EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 18); ExpressionPointer = P.parseNumericSubstitutionBlock( LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); - EXPECT_EQ(*ExpressionVal, 20U); - ExpressionPointer = - P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable, - /*IsLegacyLineExpr=*/false, - LineNumber, &Cxt, SM); + EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 20); + ExpressionPointer = P.parseNumericSubstitutionBlock( + LocalNumVar3Ref, DefinedNumericVariable, + /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); - EXPECT_EQ(*ExpressionVal, 12U); + EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 12); ASSERT_THAT_EXPECTED(EmptyVar, Succeeded()); EXPECT_EQ(*EmptyVar, ""); expectUndefErrors({std::string(UnknownVarStr)}, UnknownVar.takeError()); @@ -1123,7 +1382,7 @@ TEST_F(FileCheckTest, FileCheckContext) { ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); - EXPECT_EQ(*ExpressionVal, 36U); + EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36); // Clear local variables and check global variables remain defined. Cxt.clearLocalVars(); @@ -1135,6 +1394,6 @@ TEST_F(FileCheckTest, FileCheckContext) { ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); - EXPECT_EQ(*ExpressionVal, 36U); + EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36); } } // namespace |