diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -660,8 +660,8 @@ * ``%`` 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``. * ```` is the name of the numeric variable to define to the matching value. @@ -692,8 +692,11 @@ * 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. + literal and have a 64-bit precision. The supported operators are ``+`` and + ``-``. Spaces are accepted before, after and between any of these elements. + Evaluation of an expression follows the C language integer promotion rules, + i.e. a signed 64-bit operand is promoted to an unsigned 64-bit operand if the + other operand is unsigned. For example: diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- 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 #include @@ -31,6 +32,8 @@ return StringRef(""); 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 @@ 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,177 @@ } Expected -ExpressionFormat::getMatchingString(uint64_t IntegerValue) const { +ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const { + if (Value == Kind::Signed) { + Expected SignedValue = IntegerValue.getSignedValue(); + if (!SignedValue) + return SignedValue.takeError(); + return itostr(*SignedValue); + } + + Expected 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 +Expected 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, ValueIsSigned); + } + 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 ExpressionValue(UnsignedValue, ValueIsSigned); +} + +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; +} + +bool ExpressionValue::isNegative() const { + if (!Signed) + return false; + + return getAsSigned(Value) < 0; +} + +Expected ExpressionValue::getSignedValue() const { + if (Signed) + return getAsSigned(Value); + + if (Value > std::numeric_limits::max()) + return make_error(); + + // Value is in the representable range of int64_t so we can use cast. + return static_cast(Value); +} + +Expected ExpressionValue::getUnsignedValue() const { + if (isNegative()) + return make_error(); + + return Value; +} + +ExpressionValue ExpressionValue::getAbsolute() const { + if (!Signed) + return *this; + + if (!isNegative()) + return ExpressionValue(Value, /*Signed=*/false); + + return ExpressionValue(-getAsSigned(Value), /*Signed=*/false); +} - return IntegerValue; +Expected llvm::operator+(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + bool ResultIsSigned = LeftOperand.isSigned() && RightOperand.isSigned(); + + if (ResultIsSigned) { + int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); + int64_t RightValue = cantFail(RightOperand.getSignedValue()); + Optional Result = checkedAdd(LeftValue, RightValue); + if (!Result) + return make_error(); + + return ExpressionValue(*Result, ResultIsSigned); + } + + // Result is unsigned from now on due to operand promotion. + + // (-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 Result = + checkedAddUnsigned(LeftValue, RightValue); + if (!Result) + return make_error(); + + return ExpressionValue(*Result, ResultIsSigned); } -Expected NumericVariableUse::eval() const { - Optional Value = Variable->getValue(); +Expected llvm::operator-(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + bool ResultIsSigned = LeftOperand.isSigned() && RightOperand.isSigned(); + + if (ResultIsSigned) { + int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); + int64_t RightValue = cantFail(RightOperand.getSignedValue()); + Optional Result = checkedSub(LeftValue, RightValue); + if (!Result) + return make_error(); + + return ExpressionValue(*Result, ResultIsSigned); + } + + // Result is unsigned from now on due to operand promotion. + + // Result will be negative. + if (LeftOperand.isNegative()) + return make_error(); + + // 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 Result = + checkedOp(LeftValue, RightValue, &llvm::APInt::usub_ov); + if (!Result) + return make_error(); + return ExpressionValue(*Result, ResultIsSigned); +} + +Expected NumericVariableUse::eval() const { + Optional Value = Variable->getValue(); if (Value) return *Value; return make_error(getExpressionStr()); } -Expected BinaryOperation::eval() const { - Expected LeftOp = LeftOperand->eval(); - Expected RightOp = RightOperand->eval(); +Expected BinaryOperation::eval() const { + Expected LeftOp = LeftOperand->eval(); + Expected RightOp = RightOperand->eval(); // Bubble up any error (e.g. undefined variables) in the recursive // evaluation. @@ -136,7 +275,8 @@ Expected NumericSubstitution::getResult() const { assert(ExpressionPointer->getAST() != nullptr && "Substituting empty expression"); - Expected EvaluatedValue = ExpressionPointer->getAST()->eval(); + Expected EvaluatedValue = + ExpressionPointer->getAST()->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); ExpressionFormat Format = ExpressionPointer->getFormat(); @@ -192,6 +332,7 @@ return C; } +char OverflowError::ID = 0; char UndefVarError::ID = 0; char ErrorDiagnostic::ID = 0; char NotFoundError::ID = 0; @@ -288,26 +429,23 @@ } // 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 (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue)) + return std::make_unique(SaveExpr.drop_back(Expr.size()), + SignedLiteralValue); + Expr = SaveExpr; if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, - LiteralValue)) { - return std::make_unique( - OperandExpr.drop_back(Expr.size()), LiteralValue); - } + UnsignedLiteralValue)) + return std::make_unique(SaveExpr.drop_back(Expr.size()), + UnsignedLiteralValue); return ErrorDiagnostic::get(SM, Expr, "invalid operand format '" + Expr + "'"); } -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> Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, std::unique_ptr LeftOp, @@ -324,10 +462,10 @@ binop_eval_t EvalBinop; switch (Operator) { case '+': - EvalBinop = add; + EvalBinop = operator+; break; case '-': - EvalBinop = sub; + EvalBinop = operator-; break; default: return ErrorDiagnostic::get( @@ -376,6 +514,9 @@ case 'u': ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned); break; + case 'd': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed); + break; case 'x': ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower); break; @@ -780,7 +921,8 @@ if (!Substitutions.empty()) { TmpStr = RegExStr; if (LineNumber) - Context->LineVariable->setValue(*LineNumber); + Context->LineVariable->setValue( + ExpressionValue(*LineNumber, /*Signed=*/false)); size_t InsertOffset = 0; // Substitute all string variables and expressions whose values are only @@ -789,8 +931,18 @@ for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). Expected 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, @@ -831,7 +983,8 @@ StringRef MatchedValue = MatchInfo[CaptureParenGroup]; ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat(); - Expected Value = Format.valueFromStringRepr(MatchedValue, SM); + Expected Value = + Format.valueFromStringRepr(MatchedValue, SM); if (!Value) return Value.takeError(); DefinedNumericVariable->setValue(*Value); @@ -875,17 +1028,20 @@ // 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 \""; @@ -2047,7 +2203,7 @@ // 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 Value = Expression->getAST()->eval(); + Expected 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 --- a/llvm/lib/Support/FileCheckImpl.h +++ b/llvm/lib/Support/FileCheckImpl.h @@ -31,6 +31,8 @@ // 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 @@ NoFormat, /// Value is an unsigned integer and should be printed as a decimal number. Unsigned, + /// Value is an unsigned 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,66 @@ Expected 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 getMatchingString(uint64_t Value) const; + /// by this instance, or an error if conversion to this format failed or the + /// format is NoFormat. + Expected 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 valueFromStringRepr(StringRef StrVal, - const SourceMgr &SM) const; + Expected valueFromStringRepr(StringRef StrVal, + const SourceMgr &SM) const; +}; + +/// Class to represent an overflow error that might result when manipulating a +/// value. +class OverflowError : public ErrorInfo { +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 Signed; + +public: + explicit ExpressionValue(uint64_t Val, bool Signed) + : Value(Val), Signed(Signed) {} + + bool isSigned() const { return Signed; } + + /// Returns true is value is signed and negative, false otherwise. + bool isNegative() const; + + /// \returns the value as a signed integer or an error if the value is out of + /// range. + Expected getSignedValue() const; + + /// \returns the value as an unsigned integer or an error if the value is out + /// of range. + Expected 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 operator+(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); +Expected operator-(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); + /// Base class representing the AST of a given expression. class ExpressionAST { private: @@ -105,7 +158,7 @@ /// Evaluates and \returns the value of the expression represented by this /// AST or an error if evaluation fails. - virtual Expected eval() const = 0; + virtual Expected 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 +174,15 @@ 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) - : ExpressionAST(ExpressionStr), Value(Val) {} + template + explicit ExpressionLiteral(StringRef ExpressionStr, T Val) + : ExpressionAST(ExpressionStr), Value(Val, std::is_signed::value) {} /// \returns the literal's value. - Expected eval() const override { return Value; } + Expected eval() const override { return Value; } }; /// Class to represent an undefined variable error, which quotes that @@ -190,7 +242,7 @@ ExpressionFormat ImplicitFormat; /// Value of numeric variable, if defined, or None otherwise. - Optional Value; + Optional 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 +265,10 @@ ExpressionFormat getImplicitFormat() const { return ImplicitFormat; } /// \returns this variable's value. - Optional getValue() const { return Value; } + Optional 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 +290,7 @@ NumericVariableUse(StringRef Name, NumericVariable *Variable) : ExpressionAST(Name), Variable(Variable) {} /// \returns the value of the variable referenced by this instance. - Expected eval() const override; + Expected eval() const override; /// \returns implicit format of this numeric variable. Expected @@ -248,7 +300,8 @@ }; /// Type of functions evaluating a given binary operation. -using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); +using binop_eval_t = Expected (*)(const ExpressionValue &, + const ExpressionValue &); /// Class representing a single binary operation in the AST of an expression. class BinaryOperation : public ExpressionAST { @@ -275,7 +328,7 @@ /// 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 eval() const override; + Expected 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 --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -29,9 +29,11 @@ DEF FMT c D +-30 CHECK-LABEL: DEF FMT CHECK-NEXT: [[#%x,VAR2:]] CHECK-NEXT: [[#%X,VAR3:]] +CHECK-NEXT: [[#%d,VAR4:]] ; Numeric variable definition with explicit matching format with different ; spacing. @@ -64,24 +66,28 @@ ; Numeric expressions in explicit matching format and default matching rule using ; variables defined on other lines without spaces. USE DEF FMT IMPL MATCH -11 // VAR1 -12 // VAR1 + 1 -10 // VAR1 - 1 -c // VAR2 -d // VAR2 + 1 -b // VAR2 - 1 -1a // VAR2 + 0xe -1a // VAR2 + 0xE -D // VAR3 -E // VAR3 + 1 -C // VAR3 - 1 -1B // VAR3 + 0xe -1B // VAR3 - oxE -11 // VAR1a -11 // VAR1b -11 // VAR1c - -c // VAR2a +11 // VAR1 +12 // VAR1 + 1 +10 // VAR1 - 1 +c // VAR2 +d // VAR2 + 1 +b // VAR2 - 1 +1a // VAR2 + 0xe +1a // VAR2 + 0xE +D // VAR3 +E // VAR3 + 1 +C // VAR3 - 1 +1B // VAR3 + 0xe +1B // VAR3 + 0xE +-30 // VAR4 +-29 // VAR4 + 1 +-31 // VAR4 - 1 +42 // VAR4 + 72 +11 // VAR1a +11 // VAR1b +11 // VAR1c + +c // VAR2a CHECK-LABEL: USE DEF FMT IMPL MATCH CHECK-NEXT: [[#%u,VAR1]] CHECK-NEXT: [[#%u,VAR1+1]] @@ -96,6 +102,10 @@ CHECK-NEXT: [[#%X,VAR3-1]] CHECK-NEXT: [[#%X,VAR3+0xe]] CHECK-NEXT: [[#%X,VAR3+0xE]] +CHECK-NEXT: [[#%d,VAR4]] +CHECK-NEXT: [[#%d,VAR4+1]] +CHECK-NEXT: [[#%d,VAR4-1]] +CHECK-NEXT: [[#%d,VAR4+72]] CHECK-NEXT: [[#%u,VAR1a]] CHECK-NEXT: [[#%u,VAR1b]] CHECK-NEXT: [[#%u,VAR1c]] @@ -140,19 +150,22 @@ ; Numeric expressions in implicit matching format and default matching rule using ; variables defined on other lines. USE IMPL FMT IMPL MATCH -11 // VAR1 -12 // VAR1 + 1 -10 // VAR1 - 1 -c // VAR2 -d // VAR2 + 1 -b // VAR2 - 1 -1a // VAR2 + 0xe -1a // VAR2 + 0xE -D // VAR3 -E // VAR3 + 1 -C // VAR3 - 1 -1B // VAR3 + 0xe -1B // VAR3 + 0xE +11 // VAR1 +12 // VAR1 + 1 +10 // VAR1 - 1 +c // VAR2 +d // VAR2 + 1 +b // VAR2 - 1 +1a // VAR2 + 0xe +1a // VAR2 + 0xE +D // VAR3 +E // VAR3 + 1 +C // VAR3 - 1 +1B // VAR3 + 0xe +1B // VAR3 + 0xE +-30 // VAR4 +-29 // VAR4 + 1 +-31 // VAR4 - 1 CHECK-LABEL: USE IMPL FMT IMPL MATCH CHECK-NEXT: [[#VAR1]] CHECK-NEXT: [[#VAR1+1]] @@ -167,6 +180,9 @@ CHECK-NEXT: [[#VAR3-1]] CHECK-NEXT: [[#VAR3+0xe]] CHECK-NEXT: [[#VAR3+0xE]] +CHECK-NEXT: [[#VAR4]] +CHECK-NEXT: [[#VAR4+1]] +CHECK-NEXT: [[#VAR4-1]] ; Numeric expressions using variables defined on other lines and an immediate ; interpreted as an unsigned value. @@ -181,17 +197,29 @@ USE CONV FMT IMPL MATCH b // %x, VAR1 B // %X, VAR1 +11 // %d, VAR1 12 // %u, VAR2 C // %X, VAR2 +12 // %d, VAR2 13 // %u, VAR3 d // %x, VAR3 +13 // %d, VAR3 +15 // %u, VAR4 + 45 +f // %x, VAR4 + 45 +F // %X, VAR4 + 45 CHECK-LABEL: USE CONV FMT IMPL MATCH CHECK-NEXT: [[# %x, VAR1]] CHECK-NEXT: [[# %X, VAR1]] +CHECK-NEXT: [[# %d, VAR1]] CHECK-NEXT: [[# %u, VAR2]] CHECK-NEXT: [[# %X, VAR2]] +CHECK-NEXT: [[# %d, VAR2]] CHECK-NEXT: [[# %u, VAR3]] CHECK-NEXT: [[# %x, VAR3]] +CHECK-NEXT: [[# %d, VAR3]] +CHECK-NEXT: [[# %u, VAR4+45]] +CHECK-NEXT: [[# %x, VAR4+45]] +CHECK-NEXT: [[# %X, VAR4+45]] ; Conflicting implicit format. RUN: %ProtectFileCheckOutput \ @@ -204,17 +232,17 @@ VAR USE IMPL FMT CONFLICT 23 FMT-CONFLICT1-LABEL: VAR USE IMPL FMT CONFLICT -FMT-CONFLICT1-NEXT: [[#VAR1 + VAR2]] -FMT-CONFLICT1-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'VAR1' (%u) and 'VAR2' (%x), need an explicit format specifier -FMT-CONFLICT1-MSG-NEXT: {{F}}MT-CONFLICT1-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}} +FMT-CONFLICT1-NEXT: [[#VAR1 + VAR4]] +FMT-CONFLICT1-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'VAR1' (%u) and 'VAR4' (%d), need an explicit format specifier +FMT-CONFLICT1-MSG-NEXT: {{F}}MT-CONFLICT1-NEXT: {{\[\[#VAR1 \+ VAR4\]\]}} FMT-CONFLICT1-MSG-NEXT: {{^ \^$}} VAR USE IMPL FMT CONFLICT COMPLEX 34 FMT-CONFLICT2-LABEL: VAR USE IMPL FMT CONFLICT -FMT-CONFLICT2-NEXT: [[#VAR1 + VAR1a + VAR2]] -FMT-CONFLICT2-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'VAR1 + VAR1a' (%u) and 'VAR2' (%x), need an explicit format specifier -FMT-CONFLICT2-MSG-NEXT: {{F}}MT-CONFLICT2-NEXT: {{\[\[#VAR1 \+ VAR1a \+ VAR2\]\]}} +FMT-CONFLICT2-NEXT: [[#VAR1 + VAR1a + VAR4]] +FMT-CONFLICT2-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'VAR1 + VAR1a' (%u) and 'VAR4' (%d), need an explicit format specifier +FMT-CONFLICT2-MSG-NEXT: {{F}}MT-CONFLICT2-NEXT: {{\[\[#VAR1 \+ VAR1a \+ VAR4\]\]}} FMT-CONFLICT2-MSG-NEXT: {{^ \^$}} ; Explicitly specified format can override conflicting implicit formats. @@ -228,8 +256,8 @@ 31 42 CHECK-LABEL: USE MULTI VAR -CHECK-NEXT: [[#VAR4:]] -CHECK-NEXT: [[#VAR1+VAR4]] +CHECK-NEXT: [[#VAR5:]] +CHECK-NEXT: [[#VAR1+VAR5]] ; Numeric expression using a variable defined from a numeric expression. DEF EXPR GOOD MATCH @@ -396,3 +424,27 @@ 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,VAR1:\]\]}} 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 --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -91,10 +91,11 @@ TEST_P(ExpressionFormatParameterisedFixture, Format) { SourceMgr SM; ExpressionFormat Format(Kind); + bool Signed = Kind == ExpressionFormat::Kind::Signed; Expected 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 +104,14 @@ 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 +130,78 @@ EXPECT_FALSE(WildcardRegex.match("A")); } - Expected MatchingString = Format.getMatchingString(0U); + Expected MatchingString = + Format.getMatchingString(ExpressionValue(0, /*Signed=*/false)); ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); EXPECT_EQ(*MatchingString, "0"); - MatchingString = Format.getMatchingString(9U); + MatchingString = + Format.getMatchingString(ExpressionValue(9, /*Signed=*/false)); ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); EXPECT_EQ(*MatchingString, "9"); - Expected TenMatchingString = Format.getMatchingString(10U); + MatchingString = + Format.getMatchingString(ExpressionValue(-5, /*Signed=*/true)); + if (Signed) { + ASSERT_THAT_EXPECTED(MatchingString, Succeeded()); + EXPECT_EQ(*MatchingString, "-5"); + } else { + // Error message tested in ExpressionValue unit tests. + EXPECT_THAT_EXPECTED(MatchingString, Failed()); + } + uint64_t MaxUint64 = std::numeric_limits::max(); + Expected MaxUint64MatchingString = + Format.getMatchingString(ExpressionValue(MaxUint64, /*Signed=*/false)); + Expected TenMatchingString = + Format.getMatchingString(ExpressionValue(10, /*Signed=*/false)); ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded()); - Expected FifteenMatchingString = Format.getMatchingString(15U); + Expected FifteenMatchingString = + Format.getMatchingString(ExpressionValue(15, /*Signed=*/false)); 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 Val = + Expected 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 +218,27 @@ 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 +250,8 @@ ::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 +261,10 @@ ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); expectError("trying to match value with invalid format", NoFormat.getWildcardRegex().takeError()); - expectError("trying to match value with invalid format", - NoFormat.getMatchingString(18).takeError()); + expectError( + "trying to match value with invalid format", + NoFormat.getMatchingString(ExpressionValue(18, /*Signed=*/false)) + .takeError()); EXPECT_FALSE(bool(NoFormat)); } @@ -238,31 +295,168 @@ EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat); } +TEST_F(FileCheckTest, ExpressionValue) { + // isSigned() and isNegative() tests. + ExpressionValue UnsignedTenValue(10, /*Signed=*/false); + EXPECT_FALSE(UnsignedTenValue.isSigned()); + EXPECT_FALSE(UnsignedTenValue.isNegative()); + + ExpressionValue SignedTenValue(10, /*Signed=*/true); + EXPECT_TRUE(SignedTenValue.isSigned()); + EXPECT_FALSE(SignedTenValue.isNegative()); + + ExpressionValue MinusTenValue(-10, /*Signed=*/true); + EXPECT_TRUE(MinusTenValue.isSigned()); + EXPECT_TRUE(MinusTenValue.isNegative()); + + // getUnsignedValue() tests. + Expected UnsignedValue = UnsignedTenValue.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, 10U); + + UnsignedValue = SignedTenValue.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, 10U); + + uint64_t MaxUint64 = std::numeric_limits::max(); + ExpressionValue MaxUint64Value(MaxUint64, /*Signed=*/false); + UnsignedValue = MaxUint64Value.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, MaxUint64); + + expectError("overflow error", + MinusTenValue.getUnsignedValue().takeError()); + + // getSignedValue() tests. + Expected SignedValue = UnsignedTenValue.getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, 10); + + SignedValue = SignedTenValue.getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, 10); + + expectError("overflow error", + MaxUint64Value.getSignedValue().takeError()); + + SignedValue = MinusTenValue.getSignedValue(); + ASSERT_THAT_EXPECTED(SignedValue, Succeeded()); + EXPECT_EQ(*SignedValue, -10); + + // getAbsolute() tests. + ExpressionValue AbsoluteTenValue = UnsignedTenValue.getAbsolute(); + EXPECT_FALSE(AbsoluteTenValue.isSigned()); + UnsignedValue = AbsoluteTenValue.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, 10U); + + AbsoluteTenValue = SignedTenValue.getAbsolute(); + EXPECT_FALSE(AbsoluteTenValue.isSigned()); + UnsignedValue = AbsoluteTenValue.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, 10U); + + AbsoluteTenValue = MinusTenValue.getAbsolute(); + EXPECT_FALSE(AbsoluteTenValue.isSigned()); + UnsignedValue = AbsoluteTenValue.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedValue, Succeeded()); + EXPECT_EQ(*UnsignedValue, 10U); + + // operator+ tests. + Expected AdditionResult = MinusTenValue + MinusTenValue; + ASSERT_THAT_EXPECTED(AdditionResult, Succeeded()); + EXPECT_TRUE(AdditionResult->isNegative()); + Expected AdditionSignedValue = AdditionResult->getSignedValue(); + ASSERT_THAT_EXPECTED(AdditionSignedValue, Succeeded()); + EXPECT_EQ(*AdditionSignedValue, -20); + + ExpressionValue MinInt64Value(std::numeric_limits::min(), + /*Signed=*/true); + AdditionResult = MinInt64Value + MinusTenValue; + expectError("overflow error", AdditionResult.takeError()); + + AdditionResult = MinusTenValue + UnsignedTenValue; + ASSERT_THAT_EXPECTED(AdditionResult, Succeeded()); + EXPECT_FALSE(AdditionResult->isNegative()); + Expected AdditionUnsignedValue = AdditionResult->getUnsignedValue(); + ASSERT_THAT_EXPECTED(AdditionUnsignedValue, Succeeded()); + EXPECT_EQ(*AdditionUnsignedValue, 0U); + + AdditionResult = UnsignedTenValue + MinusTenValue; + ASSERT_THAT_EXPECTED(AdditionResult, Succeeded()); + EXPECT_FALSE(AdditionResult->isNegative()); + AdditionUnsignedValue = AdditionResult->getUnsignedValue(); + ASSERT_THAT_EXPECTED(AdditionUnsignedValue, Succeeded()); + EXPECT_EQ(*AdditionUnsignedValue, 0U); + + AdditionResult = UnsignedTenValue + UnsignedTenValue; + ASSERT_THAT_EXPECTED(AdditionResult, Succeeded()); + EXPECT_FALSE(AdditionResult->isNegative()); + AdditionUnsignedValue = AdditionResult->getUnsignedValue(); + ASSERT_THAT_EXPECTED(AdditionUnsignedValue, Succeeded()); + EXPECT_EQ(*AdditionUnsignedValue, 20U); + + AdditionResult = MaxUint64Value + UnsignedTenValue; + expectError("overflow error", AdditionResult.takeError()); + + // operator- tests. + Expected SubstractionResult = MinusTenValue - SignedTenValue; + ASSERT_THAT_EXPECTED(SubstractionResult, Succeeded()); + EXPECT_TRUE(SubstractionResult->isNegative()); + Expected SubstractionSignedValue = + SubstractionResult->getSignedValue(); + ASSERT_THAT_EXPECTED(SubstractionSignedValue, Succeeded()); + EXPECT_EQ(*SubstractionSignedValue, -20); + + SubstractionResult = MinInt64Value - SignedTenValue; + expectError("overflow error", SubstractionResult.takeError()); + + SubstractionResult = MinusTenValue - UnsignedTenValue; + expectError("overflow error", SubstractionResult.takeError()); + + SubstractionResult = UnsignedTenValue - SignedTenValue; + ASSERT_THAT_EXPECTED(SubstractionResult, Succeeded()); + EXPECT_FALSE(SubstractionResult->isNegative()); + Expected SubstractionUnsignedValue = + SubstractionResult->getUnsignedValue(); + ASSERT_THAT_EXPECTED(SubstractionUnsignedValue, Succeeded()); + EXPECT_EQ(*SubstractionUnsignedValue, 0U); +} + TEST_F(FileCheckTest, Literal) { SourceMgr SM; // Eval returns the literal's value. - ExpressionLiteral Ten(bufferize(SM, "10"), 10); - Expected Value = Ten.eval(); + ExpressionLiteral Ten(bufferize(SM, "10"), 10u); + Expected Value = Ten.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(10U, *Value); + EXPECT_EQ(10, cantFail(Value->getSignedValue())); Expected ImplicitFormat = Ten.getImplicitFormat(SM); ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::NoFormat); + // Min value can be correctly represented. + int64_t MinInt64 = std::numeric_limits::min(); + ExpressionLiteral Min(bufferize(SM, std::to_string(MinInt64)), MinInt64); + Value = Min.eval(); + ASSERT_TRUE(bool(Value)); + EXPECT_EQ(std::numeric_limits::min(), + cantFail(Value->getSignedValue())); + // Max value can be correctly represented. uint64_t MaxUint64 = std::numeric_limits::max(); ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64); Value = Max.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(std::numeric_limits::max(), *Value); + EXPECT_EQ(std::numeric_limits::max(), + cantFail(Value->getUnsignedValue())); } TEST_F(FileCheckTest, Expression) { SourceMgr SM; std::unique_ptr Ten = - std::make_unique(bufferize(SM, "10"), 10); + std::make_unique(bufferize(SM, "10"), 10u); ExpressionLiteral *TenPtr = Ten.get(); Expression Expr(std::move(Ten), ExpressionFormat(ExpressionFormat::Kind::HexLower)); @@ -283,7 +477,20 @@ EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames); } -uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } +Expected doAdd(const ExpressionValue &Op1, + const ExpressionValue &Op2) { + if (Op1.isSigned()) { + int64_t Val1 = cantFail(Op1.getSignedValue()); + int64_t Val2 = cantFail(Op2.getSignedValue()); + + return ExpressionValue(Val1 + Val2, /*Signed=*/true); + } else { + uint64_t Val1 = cantFail(Op1.getUnsignedValue()); + uint64_t Val2 = cantFail(Op2.getUnsignedValue()); + + return ExpressionValue(Val1 + Val2, /*Signed=*/false); + } +} TEST_F(FileCheckTest, NumericVariable) { SourceMgr SM; @@ -299,18 +506,18 @@ ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); EXPECT_FALSE(FooVar.getValue()); - Expected EvalResult = FooVarUse.eval(); + Expected EvalResult = FooVarUse.eval(); expectUndefErrors({"FOO"}, EvalResult.takeError()); - FooVar.setValue(42); + FooVar.setValue(ExpressionValue(42, /*Signed=*/false)); // Defined variable: getValue and eval return value set. - Optional Value = FooVar.getValue(); + Optional 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,13 +534,13 @@ StringRef FooStr = ExprStr.take_front(3); NumericVariable FooVar(FooStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - FooVar.setValue(42); + FooVar.setValue(ExpressionValue(42, /*Signed=*/false)); std::unique_ptr FooVarUse = std::make_unique(FooStr, &FooVar); StringRef BarStr = ExprStr.take_back(3); NumericVariable BarVar(BarStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2); - BarVar.setValue(18); + BarVar.setValue(ExpressionValue(18, /*Signed=*/false)); std::unique_ptr BarVarUse = std::make_unique(BarStr, &BarVar); BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse), @@ -341,9 +548,9 @@ // Defined variables: eval returns right value; implicit format is as // expected. - Expected Value = Binop.eval(); + Expected Value = Binop.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(60U, *Value); + EXPECT_EQ(60, cantFail(Value->getSignedValue())); Expected ImplicitFormat = Binop.getImplicitFormat(SM); ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); @@ -366,7 +573,7 @@ StringRef EighteenStr = ExprStr.take_back(2); FooVarUse = std::make_unique(FooStr, &FooVar); std::unique_ptr Eighteen = - std::make_unique(EighteenStr, 18); + std::make_unique(EighteenStr, 18u); Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse), std::move(Eighteen)); ImplicitFormat = Binop.getImplicitFormat(SM); @@ -376,7 +583,7 @@ FooStr = ExprStr.take_back(3); EighteenStr = ExprStr.take_front(2); FooVarUse = std::make_unique(FooStr, &FooVar); - Eighteen = std::make_unique(EighteenStr, 18); + Eighteen = std::make_unique(EighteenStr, 18u); Binop = BinaryOperation(ExprStr, doAdd, std::move(Eighteen), std::move(FooVarUse)); ImplicitFormat = Binop.getImplicitFormat(SM); @@ -655,6 +862,15 @@ // Valid single operand expression. EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("18"), Succeeded()); + uint64_t MaxUint64 = std::numeric_limits::max(); + EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MaxUint64)), + Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("0x12"), Succeeded()); + EXPECT_THAT_EXPECTED(Tester.parseSubst("-30"), Succeeded()); + int64_t MinInt64 = std::numeric_limits::min(); + EXPECT_THAT_EXPECTED(Tester.parseSubst(std::to_string(MinInt64)), + Succeeded()); // Invalid format. expectDiagnosticError("invalid matching format specification in expression", @@ -697,6 +913,7 @@ // 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()); @@ -764,7 +981,14 @@ 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()); @@ -860,7 +1084,7 @@ // substituted for the variable's value. NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - NVar.setValue(10); + NVar.setValue(ExpressionValue(10, /*Signed=*/false)); auto NVarUse = std::make_unique("N", &NVar); auto ExpressionN = std::make_unique( std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper)); @@ -970,24 +1194,24 @@ Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); - Expected ExpressionVal = (*ExpressionPointer)->getAST()->eval(); + Expected 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()); @@ -1037,7 +1261,7 @@ 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(); @@ -1049,6 +1273,6 @@ 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