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,10 +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. - 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 --- 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,188 @@ } 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); + } + 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 NumericVariableUse::eval() const { - Optional 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 ExpressionValue::getSignedValue() const { + if (Negative) + 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 (Negative) + return make_error(); + + return Value; +} + +ExpressionValue ExpressionValue::getAbsolute() const { + if (!Negative) + return *this; + + int64_t SignedValue = getAsSigned(Value); + int64_t MaxInt64 = std::numeric_limits::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 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 Result = checkedAdd(LeftValue, RightValue); + if (!Result) + return make_error(); + + 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 Result = + checkedAddUnsigned(LeftValue, RightValue); + if (!Result) + return make_error(); + + return ExpressionValue(*Result); +} + +Expected 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::max()) + return make_error(); + Optional Result = + checkedSub(LeftValue, static_cast(RightValue)); + if (!Result) + return make_error(); + + 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::max(); + // Value might underflow. + if (AbsoluteDifference > MaxInt64) { + AbsoluteDifference -= MaxInt64; + int64_t Result = -MaxInt64; + int64_t MinInt64 = std::numeric_limits::min(); + // Underflow, tested by: + // abs(Result + (max int64_t)) > abs((min int64_t) + (max int64_t)) + if (AbsoluteDifference > static_cast(-(MinInt64 - Result))) + return make_error(); + Result -= static_cast(AbsoluteDifference); + return ExpressionValue(Result); + } + + return ExpressionValue(-static_cast(AbsoluteDifference)); + } +} + +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 +286,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 +343,7 @@ return C; } +char OverflowError::ID = 0; char UndefVarError::ID = 0; char ErrorDiagnostic::ID = 0; char NotFoundError::ID = 0; @@ -295,13 +447,18 @@ } // 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( - OperandExpr.drop_back(Expr.size()), LiteralValue); - } + UnsignedLiteralValue)) + return std::make_unique(SaveExpr.drop_back(Expr.size()), + UnsignedLiteralValue); + Expr = SaveExpr; + if (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue)) + return std::make_unique(SaveExpr.drop_back(Expr.size()), + SignedLiteralValue); return ErrorDiagnostic::get(SM, Expr, "invalid operand format '" + Expr + "'"); @@ -339,14 +496,6 @@ 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> Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, std::unique_ptr LeftOp, @@ -363,10 +512,10 @@ 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 @@ 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 @@ 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 @@ 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, @@ -870,7 +1032,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); @@ -914,17 +1077,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 \""; @@ -2086,7 +2252,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 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 @@ 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 Negative; + +public: + template + 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 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 +156,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 +172,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) + template + explicit ExpressionLiteral(StringRef ExpressionStr, T Val) : ExpressionAST(ExpressionStr), Value(Val) {} /// \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 +240,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 +263,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 +288,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 +298,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 +326,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 @@ -19,8 +19,9 @@ ; 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 @@ 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 @@ 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 @@ 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-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 --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -88,13 +88,16 @@ bool AllowUpperHex; }; +const uint64_t MaxUint64 = std::numeric_limits::max(); + 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 +106,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 +132,75 @@ EXPECT_FALSE(WildcardRegex.match("A")); } - Expected MatchingString = Format.getMatchingString(0U); + Expected 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 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 MaxUint64MatchingString = + Format.getMatchingString(ExpressionValue(MaxUint64)); + Expected TenMatchingString = + Format.getMatchingString(ExpressionValue(10u)); ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded()); - Expected FifteenMatchingString = Format.getMatchingString(15U); + Expected 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 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 +217,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 +249,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 +260,9 @@ 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(18u)).takeError()); EXPECT_FALSE(bool(NoFormat)); } @@ -238,31 +293,221 @@ EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat); } +template +static Expected doValueOperation(binop_eval_t Operation, + T1 LeftValue, T2 RightValue) { + ExpressionValue LeftOperand(LeftValue); + ExpressionValue RightOperand(RightValue); + return Operation(LeftOperand, RightOperand); +} + +template +static void expectValueEqual(ExpressionValue ActualValue, T ExpectedValue) { + EXPECT_EQ(ExpectedValue < 0, ActualValue.isNegative()); + if (ExpectedValue < 0) { + Expected SignedActualValue = ActualValue.getSignedValue(); + ASSERT_THAT_EXPECTED(SignedActualValue, Succeeded()); + EXPECT_EQ(*SignedActualValue, static_cast(ExpectedValue)); + } else { + Expected UnsignedActualValue = ActualValue.getUnsignedValue(); + ASSERT_THAT_EXPECTED(UnsignedActualValue, Succeeded()); + EXPECT_EQ(*UnsignedActualValue, static_cast(ExpectedValue)); + } +} + +template +static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue, + T2 RightValue, TR ResultValue) { + Expected OperationResult = + doValueOperation(Operation, LeftValue, RightValue); + ASSERT_THAT_EXPECTED(OperationResult, Succeeded()); + expectValueEqual(*OperationResult, ResultValue); +} + +template +static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue, + T2 RightValue) { + expectError( + "overflow error", + doValueOperation(Operation, LeftValue, RightValue).takeError()); +} + +const int64_t MinInt64 = std::numeric_limits::min(); +const int64_t MaxInt64 = std::numeric_limits::max(); + +TEST_F(FileCheckTest, ExpressionValueGetUnsigned) { + // Test positive value. + Expected 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( + "overflow error", ExpressionValue(-1).getUnsignedValue().takeError()); + + // Test failure with min negative value. + expectError( + "overflow error", + ExpressionValue(MinInt64).getUnsignedValue().takeError()); +} + +TEST_F(FileCheckTest, ExpressionValueGetSigned) { + // Test positive value. + Expected 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( + "overflow error", ExpressionValue(static_cast(MaxInt64) + 1) + .getSignedValue() + .takeError()); + + // Test failure with max uint64_t. + expectError( + "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(-(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(-(MinInt64 + 10)) + 11); + + // Test both positive values with result < -(max int64_t) + expectOperationValueResult(operator-, 10, + static_cast(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 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. + 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::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(MaxUint64, 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,8 +528,6 @@ 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 @@ 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(42u)); // 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,23 +570,24 @@ StringRef FooStr = ExprStr.take_front(3); NumericVariable FooVar(FooStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - FooVar.setValue(42); + FooVar.setValue(ExpressionValue(42u)); 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(18u)); std::unique_ptr BarVarUse = std::make_unique(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 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 +610,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 +620,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 +899,13 @@ // 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 @@ // 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, 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 @@ // 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("N", &NVar); auto ExpressionN = std::make_unique( std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper)); @@ -1056,24 +1315,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()); @@ -1123,7 +1382,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(); @@ -1135,6 +1394,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