diff --git a/llvm/lib/FileCheck/FileCheck.cpp b/llvm/lib/FileCheck/FileCheck.cpp --- a/llvm/lib/FileCheck/FileCheck.cpp +++ b/llvm/lib/FileCheck/FileCheck.cpp @@ -16,6 +16,7 @@ #include "llvm/FileCheck/FileCheck.h" #include "FileCheckImpl.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/CheckedArithmetic.h" @@ -77,39 +78,34 @@ } Expected -ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const { - uint64_t AbsoluteValue; - StringRef SignPrefix = IntegerValue.isNegative() ? "-" : ""; - - if (Value == Kind::Signed) { - Expected SignedValue = IntegerValue.getSignedValue(); - if (!SignedValue) - return SignedValue.takeError(); - if (*SignedValue < 0) - AbsoluteValue = cantFail(IntegerValue.getAbsolute().getUnsignedValue()); - else - AbsoluteValue = *SignedValue; - } else { - Expected UnsignedValue = IntegerValue.getUnsignedValue(); - if (!UnsignedValue) - return UnsignedValue.takeError(); - AbsoluteValue = *UnsignedValue; - } +ExpressionFormat::getMatchingString(APInt IntegerValue) const { + if (Value != Kind::Signed && IntegerValue.isNegative()) + return make_error(); - std::string AbsoluteValueStr; + unsigned Radix; + bool UpperCase = false; + SmallString<8> AbsoluteValueStr; + StringRef SignPrefix = IntegerValue.isNegative() ? "-" : ""; switch (Value) { case Kind::Unsigned: case Kind::Signed: - AbsoluteValueStr = utostr(AbsoluteValue); + Radix = 10; break; - case Kind::HexUpper: case Kind::HexLower: - AbsoluteValueStr = utohexstr(AbsoluteValue, Value == Kind::HexLower); + Radix = 16; + UpperCase = false; + break; + case Kind::HexUpper: + Radix = 16; + UpperCase = true; break; default: return createStringError(std::errc::invalid_argument, "trying to match value with invalid format"); } + IntegerValue.abs().toString(AbsoluteValueStr, Radix, /*Signed=*/false, + /*formatAsCLiteral=*/false, + /*UpperCase=*/UpperCase); StringRef AlternateFormPrefix = AlternateForm ? StringRef("0x") : StringRef(); @@ -124,265 +120,125 @@ .str(); } -Expected -ExpressionFormat::valueFromStringRepr(StringRef StrVal, - const SourceMgr &SM) const { +unsigned nextAPIntBitWidth(unsigned BitWidth) { + return (BitWidth < APInt::APINT_BITS_PER_WORD) ? APInt::APINT_BITS_PER_WORD + : BitWidth * 2; +} + +APInt toSigned(APInt AbsVal, bool Negative) { + if (AbsVal.isSignBitSet()) + AbsVal = AbsVal.zext(nextAPIntBitWidth(AbsVal.getBitWidth())); + APInt Result = AbsVal; + if (Negative) + Result.negate(); + return Result; +} + +APInt ExpressionFormat::valueFromStringRepr(StringRef StrVal, + const SourceMgr &SM) const { bool ValueIsSigned = Value == Kind::Signed; // Both the FileCheck utility and library only call this method with a valid // value in StrVal. This is guaranteed by the regex returned by // getWildcardRegex() above. Only underflow and overflow errors can thus // occur. However new uses of this method could be added in the future so // the error message does not make assumptions about StrVal. - StringRef IntegerParseErrorStr = "unable to represent numeric value"; - if (ValueIsSigned) { - int64_t SignedValue; - - if (StrVal.getAsInteger(10, SignedValue)) - return ErrorDiagnostic::get(SM, StrVal, IntegerParseErrorStr); - - return ExpressionValue(SignedValue); - } + bool Negative = StrVal.consume_front("-"); bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; - uint64_t UnsignedValue; - bool MissingFormPrefix = AlternateForm && !StrVal.consume_front("0x"); + bool MissingFormPrefix = + !ValueIsSigned && AlternateForm && !StrVal.consume_front("0x"); (void)MissingFormPrefix; assert(!MissingFormPrefix && "missing alternate form prefix"); - if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue)) - return ErrorDiagnostic::get(SM, StrVal, IntegerParseErrorStr); - - return ExpressionValue(UnsignedValue); -} - -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 > (uint64_t)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)); + APInt ResultValue; + bool ParseFailure = StrVal.getAsInteger(Hex ? 16 : 10, ResultValue); + (void)ParseFailure; + assert(!ParseFailure && "unable to represent numeric value"); - // -X == -(max int64_t + Rem), negate each component independently. - SignedValue += MaxInt64; - uint64_t RemainingValueAbsolute = -SignedValue; - return ExpressionValue(MaxInt64 + RemainingValueAbsolute); + return toSigned(ResultValue, Negative); } -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()); - std::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()); - std::optional Result = - checkedAddUnsigned(LeftValue, RightValue); - if (!Result) - return make_error(); - - return ExpressionValue(*Result); +Expected llvm::exprAdd(const APInt &LeftOperand, + const APInt &RightOperand, bool &Overflow) { + return LeftOperand.sadd_ov(RightOperand, Overflow); } -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 > (uint64_t)std::numeric_limits::max()) - return make_error(); - std::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 llvm::exprSub(const APInt &LeftOperand, + const APInt &RightOperand, bool &Overflow) { + return LeftOperand.ssub_ov(RightOperand, Overflow); } -Expected llvm::operator*(const ExpressionValue &LeftOperand, - const ExpressionValue &RightOperand) { - // -A * -B == A * B - if (LeftOperand.isNegative() && RightOperand.isNegative()) - return LeftOperand.getAbsolute() * RightOperand.getAbsolute(); - - // A * -B == -B * A - if (RightOperand.isNegative()) - return RightOperand * LeftOperand; - - assert(!RightOperand.isNegative() && "Unexpected negative operand!"); - - // Result will be negative and can underflow. - if (LeftOperand.isNegative()) { - auto Result = LeftOperand.getAbsolute() * RightOperand.getAbsolute(); - if (!Result) - return Result; - - return ExpressionValue(0) - *Result; - } - - // Result will be positive and can overflow. - uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); - uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); - std::optional Result = - checkedMulUnsigned(LeftValue, RightValue); - if (!Result) - return make_error(); - - return ExpressionValue(*Result); +Expected llvm::exprMul(const APInt &LeftOperand, + const APInt &RightOperand, bool &Overflow) { + return LeftOperand.smul_ov(RightOperand, Overflow); } -Expected llvm::operator/(const ExpressionValue &LeftOperand, - const ExpressionValue &RightOperand) { - // -A / -B == A / B - if (LeftOperand.isNegative() && RightOperand.isNegative()) - return LeftOperand.getAbsolute() / RightOperand.getAbsolute(); - - // Check for divide by zero. - if (RightOperand == ExpressionValue(0)) - return make_error(); - - // Result will be negative and can underflow. - if (LeftOperand.isNegative() || RightOperand.isNegative()) - return ExpressionValue(0) - - cantFail(LeftOperand.getAbsolute() / RightOperand.getAbsolute()); - - uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); - uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); - return ExpressionValue(LeftValue / RightValue); +Expected llvm::exprDiv(const APInt &LeftOperand, + const APInt &RightOperand, bool &Overflow) { + if (RightOperand.isZero()) + return make_error(); + return LeftOperand.sdiv_ov(RightOperand, Overflow); } -Expected llvm::max(const ExpressionValue &LeftOperand, - const ExpressionValue &RightOperand) { - if (LeftOperand.isNegative() && RightOperand.isNegative()) { - int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); - int64_t RightValue = cantFail(RightOperand.getSignedValue()); - return ExpressionValue(std::max(LeftValue, RightValue)); - } - - if (!LeftOperand.isNegative() && !RightOperand.isNegative()) { - uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); - uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); - return ExpressionValue(std::max(LeftValue, RightValue)); - } - - if (LeftOperand.isNegative()) - return RightOperand; - - return LeftOperand; +Expected llvm::exprMax(const APInt &LeftOperand, + const APInt &RightOperand, bool &Overflow) { + Overflow = false; + return LeftOperand.slt(RightOperand) ? RightOperand : LeftOperand; } -Expected llvm::min(const ExpressionValue &LeftOperand, - const ExpressionValue &RightOperand) { - if (cantFail(max(LeftOperand, RightOperand)) == LeftOperand) - return RightOperand; - - return LeftOperand; +Expected llvm::exprMin(const APInt &LeftOperand, + const APInt &RightOperand, bool &Overflow) { + Overflow = false; + return LeftOperand.slt(RightOperand) ? LeftOperand : RightOperand; } -Expected NumericVariableUse::eval() const { - std::optional Value = Variable->getValue(); +Expected NumericVariableUse::eval() const { + std::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 MaybeLeftOp = LeftOperand->eval(); + Expected MaybeRightOp = RightOperand->eval(); // Bubble up any error (e.g. undefined variables) in the recursive // evaluation. - if (!LeftOp || !RightOp) { + if (!MaybeLeftOp || !MaybeRightOp) { Error Err = Error::success(); - if (!LeftOp) - Err = joinErrors(std::move(Err), LeftOp.takeError()); - if (!RightOp) - Err = joinErrors(std::move(Err), RightOp.takeError()); + if (!MaybeLeftOp) + Err = joinErrors(std::move(Err), MaybeLeftOp.takeError()); + if (!MaybeRightOp) + Err = joinErrors(std::move(Err), MaybeRightOp.takeError()); return std::move(Err); } - return EvalBinop(*LeftOp, *RightOp); + APInt LeftOp = *MaybeLeftOp, RightOp = *MaybeRightOp; + bool Overflow; + APInt Result; + // Ensure both operands have the same bitwidth. + unsigned LeftBitWidth = LeftOp.getBitWidth(); + unsigned RightBitWidth = RightOp.getBitWidth(); + unsigned NewBitWidth = + LeftBitWidth > RightBitWidth ? LeftBitWidth : RightBitWidth; + LeftOp = LeftOp.sext(NewBitWidth); + RightOp = RightOp.sext(NewBitWidth); + do { + Expected MaybeResult = EvalBinop(LeftOp, RightOp, Overflow); + if (!MaybeResult) + return MaybeResult.takeError(); + + if (!Overflow) { + Result = *MaybeResult; + break; + } + + NewBitWidth = nextAPIntBitWidth(LeftOp.getBitWidth()); + LeftOp = LeftOp.sext(NewBitWidth); + RightOp = RightOp.sext(NewBitWidth); + } while (Overflow); + return Result; } Expected @@ -415,8 +271,7 @@ 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(); @@ -469,7 +324,8 @@ return C; } -char OverflowError::ID = 0; +char DivByZeroError::ID = 0; +char UnderflowError::ID = 0; char UndefVarError::ID = 0; char ErrorDiagnostic::ID = 0; char NotFoundError::ID = 0; @@ -586,21 +442,18 @@ } // Otherwise, parse it as a literal. - int64_t SignedLiteralValue; - uint64_t UnsignedLiteralValue; + APInt LiteralValue; StringRef SaveExpr = Expr; - // Accept both signed and unsigned literal, default to signed literal. + bool Negative = Expr.consume_front("-"); if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, - UnsignedLiteralValue)) + LiteralValue)) { + LiteralValue = toSigned(LiteralValue, Negative); 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); + LiteralValue); + } return ErrorDiagnostic::get( - SM, Expr, + SM, SaveExpr, Twine("invalid ") + (MaybeInvalidConstraint ? "matching constraint or " : "") + "operand format"); @@ -655,10 +508,10 @@ binop_eval_t EvalBinop; switch (Operator) { case '+': - EvalBinop = operator+; + EvalBinop = exprAdd; break; case '-': - EvalBinop = operator-; + EvalBinop = exprSub; break; default: return ErrorDiagnostic::get( @@ -692,12 +545,12 @@ assert(Expr.startswith("(")); auto OptFunc = StringSwitch(FuncName) - .Case("add", operator+) - .Case("div", operator/) - .Case("max", max) - .Case("min", min) - .Case("mul", operator*) - .Case("sub", operator-) + .Case("add", exprAdd) + .Case("div", exprDiv) + .Case("max", exprMax) + .Case("min", exprMin) + .Case("mul", exprMul) + .Case("sub", exprSub) .Default(nullptr); if (!OptFunc) @@ -1244,7 +1097,8 @@ if (!Substitutions.empty()) { TmpStr = RegExStr; if (LineNumber) - Context->LineVariable->setValue(ExpressionValue(*LineNumber)); + Context->LineVariable->setValue( + APInt(sizeof(*LineNumber) * 8, *LineNumber)); size_t InsertOffset = 0; // Substitute all string variables and expressions whose values are only @@ -1261,11 +1115,17 @@ Errs = joinErrors(std::move(Errs), handleErrors( Value.takeError(), - [&](const OverflowError &E) { + [&SM, &Substitution](const UnderflowError &E) { + return ErrorDiagnostic::get( + SM, Substitution->getFromString(), + "unable to substitute variable or " + "numeric expression: underflow error"); + }, + [&SM, &Substitution](const DivByZeroError &E) { return ErrorDiagnostic::get( SM, Substitution->getFromString(), "unable to substitute variable or " - "numeric expression: overflow error"); + "numeric expression: division by error"); }, [&SM](const UndefVarError &E) { return ErrorDiagnostic::get(SM, E.getVarName(), @@ -1323,11 +1183,8 @@ StringRef MatchedValue = MatchInfo[CaptureParenGroup]; ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat(); - Expected Value = - Format.valueFromStringRepr(MatchedValue, SM); - if (!Value) - return MatchResult(TheMatch, Value.takeError()); - DefinedNumericVariable->setValue(*Value, MatchedValue); + APInt Value = Format.valueFromStringRepr(MatchedValue, SM); + DefinedNumericVariable->setValue(Value, MatchedValue); } return MatchResult(TheMatch, Error::success()); @@ -2710,7 +2567,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/FileCheck/FileCheckImpl.h b/llvm/lib/FileCheck/FileCheckImpl.h --- a/llvm/lib/FileCheck/FileCheckImpl.h +++ b/llvm/lib/FileCheck/FileCheckImpl.h @@ -15,6 +15,7 @@ #ifndef LLVM_LIB_FILECHECK_FILECHECKIMPL_H #define LLVM_LIB_FILECHECK_FILECHECKIMPL_H +#include "llvm/ADT/APInt.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/FileCheck/FileCheck.h" @@ -31,8 +32,6 @@ // 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. @@ -94,80 +93,50 @@ /// \returns the string representation of \p Value in the format represented /// by this instance, or an error if conversion to this format failed or the /// format is NoFormat. - Expected getMatchingString(ExpressionValue Value) const; + Expected getMatchingString(APInt 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; + /// according to the matching format represented by this instance. + APInt valueFromStringRepr(StringRef StrVal, const SourceMgr &SM) const; }; /// Class to represent an overflow error that might result when manipulating a /// value. -class OverflowError : public ErrorInfo { +class UnderflowError : public ErrorInfo { public: static char ID; std::error_code convertToErrorCode() const override { - return std::make_error_code(std::errc::value_too_large); + return std::make_error_code(std::errc::invalid_argument); } - void log(raw_ostream &OS) const override { OS << "overflow error"; } + void log(raw_ostream &OS) const override { OS << "underflow error"; } }; -/// Class representing a numeric value. -class ExpressionValue { -private: - uint64_t Value; - bool Negative; - +/// Class to represent a division by zero error. +class DivByZeroError : public ErrorInfo { public: - template - explicit ExpressionValue(T Val) : Value(Val), Negative(Val < 0) {} - - bool operator==(const ExpressionValue &Other) const { - return Value == Other.Value && isNegative() == Other.isNegative(); - } - - bool operator!=(const ExpressionValue &Other) const { - return !(*this == Other); - } + static char ID; - /// Returns true if value is signed and negative, false otherwise. - bool isNegative() const { - assert((Value != 0 || !Negative) && "Unexpected negative zero!"); - return Negative; + std::error_code convertToErrorCode() const override { + return std::make_error_code(std::errc::invalid_argument); } - /// \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; + void log(raw_ostream &OS) const override { OS << "division by zero error"; } }; -/// 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); -Expected operator*(const ExpressionValue &Lhs, - const ExpressionValue &Rhs); -Expected operator/(const ExpressionValue &Lhs, - const ExpressionValue &Rhs); -Expected max(const ExpressionValue &Lhs, - const ExpressionValue &Rhs); -Expected min(const ExpressionValue &Lhs, - const ExpressionValue &Rhs); +Expected exprAdd(const APInt &LeftOperand, const APInt &RightOperand, + bool &Overflow); +Expected exprSub(const APInt &LeftOperand, const APInt &RightOperand, + bool &Overflow); +Expected exprMul(const APInt &LeftOperand, const APInt &RightOperand, + bool &Overflow); +Expected exprDiv(const APInt &LeftOperand, const APInt &RightOperand, + bool &Overflow); +Expected exprMin(const APInt &LeftOperand, const APInt &RightOperand, + bool &Overflow); +Expected exprMax(const APInt &LeftOperand, const APInt &RightOperand, + bool &Overflow); /// Base class representing the AST of a given expression. class ExpressionAST { @@ -183,7 +152,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 @@ -199,15 +168,14 @@ class ExpressionLiteral : public ExpressionAST { private: /// Actual value of the literal. - ExpressionValue Value; + APInt Value; public: - template - explicit ExpressionLiteral(StringRef ExpressionStr, T Val) + explicit ExpressionLiteral(StringRef ExpressionStr, APInt 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 @@ -266,7 +234,7 @@ ExpressionFormat ImplicitFormat; /// Value of numeric variable, if defined, or std::nullopt otherwise. - std::optional Value; + std::optional Value; /// The input buffer's string from which Value was parsed, or std::nullopt. /// See comments on getStringValue for a discussion of the std::nullopt case. @@ -293,7 +261,7 @@ ExpressionFormat getImplicitFormat() const { return ImplicitFormat; } /// \returns this variable's value. - std::optional getValue() const { return Value; } + std::optional getValue() const { return Value; } /// \returns the input buffer's string from which this variable's value was /// parsed, or std::nullopt if the value is not yet defined or was not parsed @@ -305,7 +273,7 @@ /// Sets value of this numeric variable to \p NewValue, and sets the input /// buffer string from which it was parsed to \p NewStrValue. See comments on /// getStringValue for a discussion of when the latter can be std::nullopt. - void setValue(ExpressionValue NewValue, + void setValue(APInt NewValue, std::optional NewStrValue = std::nullopt) { Value = NewValue; StrValue = NewStrValue; @@ -334,7 +302,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 @@ -344,8 +312,8 @@ }; /// Type of functions evaluating a given binary operation. -using binop_eval_t = Expected (*)(const ExpressionValue &, - const ExpressionValue &); +using binop_eval_t = Expected (*)(const APInt &LHS, const APInt &RHS, + bool &Overflow); /// Class representing a single binary operation in the AST of an expression. class BinaryOperation : public ExpressionAST { @@ -372,7 +340,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/match-time-error-propagation/invalid-excluded-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/invalid-excluded-pattern.txt deleted file mode 100644 --- a/llvm/test/FileCheck/match-time-error-propagation/invalid-excluded-pattern.txt +++ /dev/null @@ -1,67 +0,0 @@ -; Check handling of match-time diagnostics for invalid patterns (e.g., -; substitution overflow) in the case of excluded patterns (e.g., CHECK-NOT). -; -; At one time, FileCheck's exit status was zero for this case. Moreover, it -; printed the error diagnostic only if -vv was specified and input dumps were -; disabled. Test every combination as the logic is hard to get right. - -RUN: echo > %t.chk \ -RUN: 'CHECK-NOT: [[#0x8000000000000000+0x8000000000000000]] [[UNDEFVAR]]' -RUN: echo > %t.in '10000000000000000' - - ERR-NOT:{{.}} - ERR-VV:{{.*}}: remark: implicit EOF: expected string found in input - ERR-VV-NEXT:CHECK-NOT: {{.*}} - ERR-VV-NEXT:{{ *}}^ - ERR-VV-NEXT:{{.*}}: note: found here -ERR-VV-EMPTY: - ERR-VV-NEXT:^ - ERR-NOT:{{.}} - ERR:{{.*}}: error: unable to substitute variable or numeric expression: overflow error - ERR-NEXT:CHECK-NOT: {{.*}} - ERR-NEXT:{{ *}}^ - ERR-NEXT:{{.*}}: error: undefined variable: UNDEFVAR - ERR-NEXT:{{CHECK-NOT: \[\[#0x8000000000000000\+0x8000000000000000\]\] \[\[UNDEFVAR\]\]}} - ERR-NEXT:^ - ERR-NOT:{{error|note|remark}}: - - DUMP:<<<<<< - DUMP-NEXT: 1: 10000000000000000 - DUMP-NEXT:not:1'0 X~~~~~~~~~~~~~~~~~ error: match failed for invalid pattern - DUMP-NEXT:not:1'1 unable to substitute variable or numeric expression: overflow error - DUMP-NEXT:not:1'2 undefined variable: UNDEFVAR -DUMP-VV-NEXT: 2: -DUMP-VV-NEXT:eof:1 ^ - DUMP-NEXT:>>>>>> - -;-------------------------------------------------- -; Check -dump-input=never cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,ERR-VV - -;-------------------------------------------------- -; Check -dump-input=fail cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP,DUMP-VV diff --git a/llvm/test/FileCheck/match-time-error-propagation/invalid-expected-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/invalid-expected-pattern.txt deleted file mode 100644 --- a/llvm/test/FileCheck/match-time-error-propagation/invalid-expected-pattern.txt +++ /dev/null @@ -1,54 +0,0 @@ -; Check handling of match-time diagnostics for invalid patterns (e.g., -; substitution overflow) in the case of expected patterns (e.g., CHECK). - -RUN: echo > %t.chk \ -RUN: 'CHECK: [[#0x8000000000000000+0x8000000000000000]] [[UNDEFVAR]]' -RUN: echo > %t.in '10000000000000000' - - ERR-NOT:{{.}} - ERR:{{.*}}: error: unable to substitute variable or numeric expression: overflow error - ERR-NEXT:CHECK: {{.*}} - ERR-NEXT:{{ *}}^ - ERR-NEXT:{{.*}}: error: undefined variable: UNDEFVAR - ERR-NEXT:{{CHECK: \[\[#0x8000000000000000\+0x8000000000000000\]\] \[\[UNDEFVAR\]\]}} - ERR-NEXT:^ - ERR-NOT:{{error|note|remark}}: - - DUMP:<<<<<< -DUMP-NEXT: 1: 10000000000000000 -DUMP-NEXT:check:1'0 X~~~~~~~~~~~~~~~~~ error: match failed for invalid pattern -DUMP-NEXT:check:1'1 unable to substitute variable or numeric expression: overflow error -DUMP-NEXT:check:1'2 undefined variable: UNDEFVAR -DUMP-NEXT:>>>>>> - -;-------------------------------------------------- -; Check -dump-input=never cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -;-------------------------------------------------- -; Check -dump-input=fail cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP diff --git a/llvm/test/FileCheck/match-time-error-propagation/matched-excluded-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/matched-excluded-pattern.txt deleted file mode 100644 --- a/llvm/test/FileCheck/match-time-error-propagation/matched-excluded-pattern.txt +++ /dev/null @@ -1,88 +0,0 @@ -; Check handling of diagnostics for problematic matches (e.g., variable capture -; overflow) in the case of excluded patterns (e.g., CHECK-NOT). -; -; At one time, FileCheck's exit status for the following example was zero even -; though the excluded pattern does match (it's just capturing that fails). -; Moreover, it printed the error diagnostic only if -vv was specified and input -; dumps were disabled. Test every combination as the logic is hard to get -; right. -; -; TODO: Capturing from an excluded pattern probably shouldn't be permitted -; because it seems useless: it's captured only if the pattern matches, but then -; FileCheck fails. The helpfulness of reporting overflow from that capture is -; perhaps questionable then, but it doesn't seem harmful either. Anyway, the -; goal of this test is simply to exercise the error propagation mechanism for a -; matched excluded pattern. In the future, if we have a more interesting error -; to exercise in that case, we should instead use it in this test, and then we -; might want to think more about where that error should be presented in the -; list of diagnostics. - -RUN: echo > %t.chk 'CHECK-NOT: [[#122+1]] [[STR:abc]] [[#NUM:]]' -RUN: echo > %t.in '123 abc 1000000000000000000000000000000000000000000000000000' - - ERR-NOT:{{.}} - ERR-VV:{{.*}}: remark: implicit EOF: expected string found in input - ERR-VV-NEXT:CHECK-NOT: {{.*}} - ERR-VV-NEXT:{{ *}}^ - ERR-VV-NEXT:{{.*}}: note: found here -ERR-VV-EMPTY: - ERR-VV-NEXT:^ - ERR-NOT:{{.}} - ERR:{{.*}}: error: CHECK-NOT: excluded string found in input - ERR-NEXT:CHECK-NOT: {{.*}} - ERR-NEXT:{{ *}}^ - ERR-NEXT::1:1: note: found here - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT:^~~~~~~~~{{~*}} - ERR-NEXT::1:1: note: with "122+1" equal to "123" - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT:^ - ERR-NEXT::1:5: note: captured var "STR" - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT: ^~~ - ERR-NEXT::1:9: error: unable to represent numeric value - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT: ^ - ERR-NOT:{{error|note|remark}}: - - DUMP:<<<<<< - DUMP-NEXT: 1: 123 abc 10{{0*}} - DUMP-NEXT:not:1'0 !~~~~~~~~~{{~*}} error: no match expected - DUMP-NEXT:not:1'1 with "122+1" equal to "123" - DUMP-NEXT:not:1'2 !~~ captured var "STR" - DUMP-NEXT:not:1'3 !~{{~*}} error: unable to represent numeric value -DUMP-VV-NEXT: 2: -DUMP-VV-NEXT:eof:1 ^ - DUMP-NEXT:>>>>>> - -;-------------------------------------------------- -; Check -dump-input=never cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,ERR-VV - -;-------------------------------------------------- -; Check -dump-input=fail cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP,DUMP-VV diff --git a/llvm/test/FileCheck/match-time-error-propagation/matched-expected-pattern.txt b/llvm/test/FileCheck/match-time-error-propagation/matched-expected-pattern.txt deleted file mode 100644 --- a/llvm/test/FileCheck/match-time-error-propagation/matched-expected-pattern.txt +++ /dev/null @@ -1,63 +0,0 @@ -; Check handling of diagnostics for problematic matches (e.g., variable capture -; overflow) in the case of expected patterns (e.g., CHECK). - -RUN: echo > %t.chk 'CHECK: [[#122+1]] [[STR:abc]] [[#NUM:]]' -RUN: echo > %t.in '123 abc 1000000000000000000000000000000000000000000000000000' - - ERR-NOT:{{.}} - ERR:{{.*}}: remark: CHECK: expected string found in input - ERR-NEXT:CHECK: {{.*}} - ERR-NEXT:{{ *}}^ - ERR-NEXT::1:1: note: found here - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT:^~~~~~~~~{{~*}} - ERR-NEXT::1:1: note: with "122+1" equal to "123" - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT:^ - ERR-NEXT::1:5: note: captured var "STR" - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT: ^~~ - ERR-NEXT::1:9: error: unable to represent numeric value - ERR-NEXT:123 abc 10{{0*}} - ERR-NEXT: ^ - ERR-NOT:{{error|note|remark}}: - - DUMP:<<<<<< -DUMP-NEXT: 1: 123 abc 10{{0*}} -DUMP-NEXT:check:1'0 ^~~~~~~~~~{{~*}} -DUMP-NEXT:check:1'1 with "122+1" equal to "123" -DUMP-NEXT:check:1'2 ^~~ captured var "STR" -DUMP-NEXT:check:1'3 !~{{~*}} error: unable to represent numeric value -DUMP-NEXT:>>>>>> - -;-------------------------------------------------- -; Check -dump-input=never cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=never -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR - -;-------------------------------------------------- -; Check -dump-input=fail cases. -;-------------------------------------------------- - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -v %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP - -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck -dump-input=fail -vv %t.chk < %t.in 2>&1 \ -RUN: | FileCheck %s -match-full-lines -check-prefixes=ERR,DUMP 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 @@ -487,19 +487,6 @@ CLI-NUM-CONFLICT-NEXT: Global define #2: #STRVAR=42 (parsed as: {{\[\[#STRVAR:42\]\]}}) CLI-NUM-CONFLICT-NEXT: {{^}} ^{{$}} -; Numeric variable definition with too big value. -RUN: %ProtectFileCheckOutput \ -RUN: not FileCheck --check-prefix BIGVAL --input-file %s %s 2>&1 \ -RUN: | FileCheck --strict-whitespace --check-prefix BIGVAL-MSG %s - -BIG VALUE -NUMVAR: 10000000000000000000000 -BIGVAL-LABEL: BIG VALUE -BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]] -BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: unable to represent numeric value - BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000 -BIGVAL-MSG-NEXT: {{^}} ^{{$}} - ; Verify that when a variable is set to an expression the expression is still ; checked. RUN: %ProtectFileCheckOutput \ @@ -557,32 +544,6 @@ REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,UNSI:\]\]}} REDEF-NEW-FMT-MSG-NEXT: {{^}} ^{{$}} -; Numeric expression with overflow. -RUN: %ProtectFileCheckOutput \ -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: %ProtectFileCheckOutput \ -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: {{^}} ^{{$}} - RUN: %ProtectFileCheckOutput \ RUN: not FileCheck -D#NUMVAR=10 --check-prefix CALL-MISSING-CLOSING-BRACKET --input-file %s %s 2>&1 \ RUN: | FileCheck --strict-whitespace --check-prefix CALL-MISSING-CLOSING-BRACKET-MSG %s diff --git a/llvm/unittests/FileCheck/FileCheckTest.cpp b/llvm/unittests/FileCheck/FileCheckTest.cpp --- a/llvm/unittests/FileCheck/FileCheckTest.cpp +++ b/llvm/unittests/FileCheck/FileCheckTest.cpp @@ -184,45 +184,39 @@ } template void checkMatchingString(T Val, StringRef ExpectedStr) { - Expected MatchingString = - Format.getMatchingString(ExpressionValue(Val)); + APInt Value(128, Val, std::is_signed_v); + Expected MatchingString = Format.getMatchingString(Value); ASSERT_THAT_EXPECTED(MatchingString, Succeeded()) - << "No matching string for " << Val; + << "No matching string for " + << (std::is_signed_v ? Value.getSExtValue() : Value.getZExtValue()); EXPECT_EQ(*MatchingString, ExpectedStr); } template void checkMatchingStringFailure(T Val) { - Expected MatchingString = - Format.getMatchingString(ExpressionValue(Val)); + APInt Value(64, Val, std::is_signed_v); + Expected MatchingString = Format.getMatchingString(Value); // Error message tested in ExpressionValue unit tests. EXPECT_THAT_EXPECTED(MatchingString, Failed()); } - Expected getValueFromStringReprFailure(StringRef Str) { + Expected getValueFromStringReprFailure(StringRef Str) { StringRef BufferizedStr = bufferize(SM, Str); return Format.valueFromStringRepr(BufferizedStr, SM); } template void checkValueFromStringRepr(StringRef Str, T ExpectedVal) { - Expected ResultValue = getValueFromStringReprFailure(Str); + Expected ResultValue = getValueFromStringReprFailure(Str); ASSERT_THAT_EXPECTED(ResultValue, Succeeded()) << "Failed to get value from " << Str; ASSERT_EQ(ResultValue->isNegative(), ExpectedVal < 0) << "Value for " << Str << " is not " << ExpectedVal; - if (ResultValue->isNegative()) - EXPECT_EQ(cantFail(ResultValue->getSignedValue()), - static_cast(ExpectedVal)); + if (std::is_signed_v) + EXPECT_EQ(ResultValue->getSExtValue(), static_cast(ExpectedVal)); else - EXPECT_EQ(cantFail(ResultValue->getUnsignedValue()), + EXPECT_EQ(ResultValue->getZExtValue(), static_cast(ExpectedVal)); } - - void checkValueFromStringReprFailure( - StringRef Str, StringRef ErrorStr = "unable to represent numeric value") { - Expected ResultValue = getValueFromStringReprFailure(Str); - expectDiagnosticError(ErrorStr, ResultValue.takeError()); - } }; TEST_P(ExpressionFormatParameterisedFixture, FormatGetWildcardRegex) { @@ -287,7 +281,7 @@ if (Signed) { checkMatchingString(-5, padWithLeadingZeros("-5")); - checkMatchingStringFailure(MaxUint64); + checkMatchingString(MaxUint64, padWithLeadingZeros(MaxUint64Str)); checkMatchingString(MaxInt64, padWithLeadingZeros(MaxInt64Str)); checkMatchingString(MinInt64, padWithLeadingZeros(MinInt64Str)); } else { @@ -309,17 +303,12 @@ if (Signed) { checkValueFromStringRepr("-5", -5); - checkValueFromStringReprFailure(MaxUint64Str); } else { checkValueFromStringRepr(addBasePrefix(MaxUint64Str), MaxUint64); } checkValueFromStringRepr(addBasePrefix(TenStr), 10); checkValueFromStringRepr(addBasePrefix(FifteenStr), 15); - - // Wrong casing is not tested because valueFromStringRepr() relies on - // StringRef's getAsInteger() which does not allow to restrict casing. - checkValueFromStringReprFailure(addBasePrefix("G")); } TEST_P(ExpressionFormatParameterisedFixture, FormatBoolOperator) { @@ -359,7 +348,7 @@ NoFormat.getWildcardRegex().takeError()); expectError( "trying to match value with invalid format", - NoFormat.getMatchingString(ExpressionValue(18u)).takeError()); + NoFormat.getMatchingString(APInt(64, 18u)).takeError()); EXPECT_FALSE(bool(NoFormat)); } @@ -390,323 +379,208 @@ 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 APInt toAPInt(T Value) { + if (std::is_unsigned_v) + return APInt(128, Value); + return APInt(128, Value, /*IsSigned=*/true); } -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); +static void expectOperationValueResult(binop_eval_t Operation, APInt LeftValue, + APInt RightValue, APInt ResultValue) { + bool Overflow; + Expected OperationResult = Operation(LeftValue, RightValue, Overflow); ASSERT_THAT_EXPECTED(OperationResult, Succeeded()); - expectValueEqual(*OperationResult, ResultValue); + EXPECT_EQ(*OperationResult, ResultValue); } -template -static void expectOperationValueResult(binop_eval_t Operation, T1 LeftValue, - T2 RightValue) { - expectError( - "overflow error", - doValueOperation(Operation, LeftValue, RightValue).takeError()); +template +static void expectOperationValueResult(binop_eval_t Operation, T1 LeftVal, + T2 RightVal, T3 ResultVal) { + APInt LeftValue(128, LeftVal, std::is_signed_v); + APInt RightValue(128, RightVal, std::is_signed_v); + APInt ResultValue = std::is_integral_v + ? APInt(128, ResultVal, std::is_signed_v) + : APInt(128, ResultVal, /*Radix=*/10); + expectOperationValueResult(Operation, LeftValue, RightValue, ResultValue); } -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); +template +static void expectOperationValueResult(binop_eval_t Operation, T1 LeftVal, + T2 RightVal) { + bool Overflow; + APInt LeftValue(128, LeftVal, std::is_signed_v); + APInt RightValue(128, RightVal, std::is_signed_v); + expectError( + "division by zero error", + Operation(LeftValue, RightValue, Overflow).takeError()); } -TEST_F(FileCheckTest, ExpressionValueAddition) { +TEST_F(FileCheckTest, ExpressionAddition) { // Test both negative values. - expectOperationValueResult(operator+, -10, -10, -20); + expectOperationValueResult(exprAdd, -10, -10, -20); - // Test both negative values with underflow. - expectOperationValueResult(operator+, MinInt64, -1); - expectOperationValueResult(operator+, MinInt64, MinInt64); + // Test both negative values with result not representable in int64_t. + expectOperationValueResult(exprAdd, MinInt64, -1, "-9223372036854775809"); + expectOperationValueResult(exprAdd, MinInt64, MinInt64, + "-18446744073709551616"); // Test negative and positive value. - expectOperationValueResult(operator+, -10, 10, 0); - expectOperationValueResult(operator+, -10, 11, 1); - expectOperationValueResult(operator+, -11, 10, -1); + expectOperationValueResult(exprAdd, -10, 10, 0); + expectOperationValueResult(exprAdd, -10, 11, 1); + expectOperationValueResult(exprAdd, -11, 10, -1); // Test positive and negative value. - expectOperationValueResult(operator+, 10, -10, 0); - expectOperationValueResult(operator+, 10, -11, -1); - expectOperationValueResult(operator+, 11, -10, 1); + expectOperationValueResult(exprAdd, 10, -10, 0); + expectOperationValueResult(exprAdd, 10, -11, -1); + expectOperationValueResult(exprAdd, 11, -10, 1); // Test both positive values. - expectOperationValueResult(operator+, 10, 10, 20); + expectOperationValueResult(exprAdd, 10, 10, 20); - // Test both positive values with overflow. - expectOperationValueResult(operator+, MaxUint64, 1); - expectOperationValueResult(operator+, MaxUint64, MaxUint64); + // Test both positive values with result not representable in uint64_t. + expectOperationValueResult(exprAdd, MaxUint64, 1, "18446744073709551616"); + expectOperationValueResult(exprAdd, MaxUint64, MaxUint64, + "36893488147419103230"); } -TEST_F(FileCheckTest, ExpressionValueSubtraction) { +TEST_F(FileCheckTest, ExpressionSubtraction) { // Test negative value and value bigger than int64_t max. - expectOperationValueResult(operator-, -10, MaxUint64); + expectOperationValueResult(exprSub, -10, MaxUint64, "-18446744073709551625"); - // Test negative and positive value with underflow. - expectOperationValueResult(operator-, MinInt64, 1); + // Test negative and positive value with result not representable in int64_t. + expectOperationValueResult(exprSub, MinInt64, 1, "-9223372036854775809"); // Test negative and positive value. - expectOperationValueResult(operator-, -10, 10, -20); + expectOperationValueResult(exprSub, -10, 10, -20); // Test both negative values. - expectOperationValueResult(operator-, -10, -10, 0); - expectOperationValueResult(operator-, -11, -10, -1); - expectOperationValueResult(operator-, -10, -11, 1); + expectOperationValueResult(exprSub, -10, -10, 0); + expectOperationValueResult(exprSub, -11, -10, -1); + expectOperationValueResult(exprSub, -10, -11, 1); // Test positive and negative values. - expectOperationValueResult(operator-, 10, -10, 20); + expectOperationValueResult(exprSub, 10, -10, 20); // Test both positive values with result positive. - expectOperationValueResult(operator-, 10, 5, 5); + expectOperationValueResult(exprSub, 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 not representable on int64_t. + expectOperationValueResult(exprSub, 0, MaxUint64, "-18446744073709551615"); + expectOperationValueResult(exprSub, 0, + static_cast(-(MinInt64 + 10)) + 11, + "-9223372036854775809"); // Test both positive values with result < -(max int64_t) - expectOperationValueResult(operator-, 10, +#if 0 + expectOperationValueResult(exprSub, 10, static_cast(MaxInt64) + 11, -MaxInt64 - 1); +#endif // Test both positive values with 0 > result > -(max int64_t) - expectOperationValueResult(operator-, 10, 11, -1); + expectOperationValueResult(exprSub, 10, 11, -1); } TEST_F(FileCheckTest, ExpressionValueMultiplication) { // Test mixed signed values. - expectOperationValueResult(operator*, -3, 10, -30); - expectOperationValueResult(operator*, 2, -17, -34); - expectOperationValueResult(operator*, 0, MinInt64, 0); - expectOperationValueResult(operator*, MinInt64, 1, MinInt64); - expectOperationValueResult(operator*, 1, MinInt64, MinInt64); - expectOperationValueResult(operator*, MaxInt64, -1, -MaxInt64); - expectOperationValueResult(operator*, -1, MaxInt64, -MaxInt64); + expectOperationValueResult(exprMul, -3, 10, -30); + expectOperationValueResult(exprMul, 2, -17, -34); + expectOperationValueResult(exprMul, 0, MinInt64, 0); + expectOperationValueResult(exprMul, MinInt64, 1, MinInt64); + expectOperationValueResult(exprMul, 1, MinInt64, MinInt64); + expectOperationValueResult(exprMul, MaxInt64, -1, -MaxInt64); + expectOperationValueResult(exprMul, -1, MaxInt64, -MaxInt64); // Test both negative values. - expectOperationValueResult(operator*, -3, -10, 30); - expectOperationValueResult(operator*, -2, -17, 34); - expectOperationValueResult(operator*, MinInt64, -1, AbsoluteMinInt64); + expectOperationValueResult(exprMul, -3, -10, 30); + expectOperationValueResult(exprMul, -2, -17, 34); + expectOperationValueResult(exprMul, MinInt64, -1, AbsoluteMinInt64); // Test both positive values. - expectOperationValueResult(operator*, 3, 10, 30); - expectOperationValueResult(operator*, 2, 17, 34); - expectOperationValueResult(operator*, 0, MaxUint64, 0); - - // Test negative results that underflow. - expectOperationValueResult(operator*, -10, MaxInt64); - expectOperationValueResult(operator*, MaxInt64, -10); - expectOperationValueResult(operator*, 10, MinInt64); - expectOperationValueResult(operator*, MinInt64, 10); - expectOperationValueResult(operator*, -1, MaxUint64); - expectOperationValueResult(operator*, MaxUint64, -1); - expectOperationValueResult(operator*, -1, AbsoluteMaxInt64 + 2); - expectOperationValueResult(operator*, AbsoluteMaxInt64 + 2, -1); - - // Test positive results that overflow. - expectOperationValueResult(operator*, 10, MaxUint64); - expectOperationValueResult(operator*, MaxUint64, 10); - expectOperationValueResult(operator*, MinInt64, -10); - expectOperationValueResult(operator*, -10, MinInt64); + expectOperationValueResult(exprMul, 3, 10, 30); + expectOperationValueResult(exprMul, 2, 17, 34); + expectOperationValueResult(exprMul, 0, MaxUint64, 0); + + // Test negative results not representable in int64_t. + expectOperationValueResult(exprMul, -10, MaxInt64, "-92233720368547758070"); + expectOperationValueResult(exprMul, MaxInt64, -10, "-92233720368547758070"); + expectOperationValueResult(exprMul, 10, MinInt64, "-92233720368547758080"); + expectOperationValueResult(exprMul, MinInt64, 10, "-92233720368547758080"); + expectOperationValueResult(exprMul, -1, MaxUint64, "-18446744073709551615"); + expectOperationValueResult(exprMul, MaxUint64, -1, "-18446744073709551615"); + expectOperationValueResult(exprMul, -1, AbsoluteMaxInt64 + 2, + "-9223372036854775809"); + expectOperationValueResult(exprMul, AbsoluteMaxInt64 + 2, -1, + "-9223372036854775809"); + + // Test positive results not representable in uint64_t. + expectOperationValueResult(exprMul, 10, MaxUint64, "184467440737095516150"); + expectOperationValueResult(exprMul, MaxUint64, 10, "184467440737095516150"); + expectOperationValueResult(exprMul, MinInt64, -10, "92233720368547758080"); + expectOperationValueResult(exprMul, -10, MinInt64, "92233720368547758080"); } TEST_F(FileCheckTest, ExpressionValueDivision) { // Test mixed signed values. - expectOperationValueResult(operator/, -30, 10, -3); - expectOperationValueResult(operator/, 34, -17, -2); - expectOperationValueResult(operator/, 0, -10, 0); - expectOperationValueResult(operator/, MinInt64, 1, MinInt64); - expectOperationValueResult(operator/, MaxInt64, -1, -MaxInt64); - expectOperationValueResult(operator/, -MaxInt64, 1, -MaxInt64); + expectOperationValueResult(exprDiv, -30, 10, -3); + expectOperationValueResult(exprDiv, 34, -17, -2); + expectOperationValueResult(exprDiv, 0, -10, 0); + expectOperationValueResult(exprDiv, MinInt64, 1, MinInt64); + expectOperationValueResult(exprDiv, MaxInt64, -1, -MaxInt64); + expectOperationValueResult(exprDiv, -MaxInt64, 1, -MaxInt64); // Test both negative values. - expectOperationValueResult(operator/, -30, -10, 3); - expectOperationValueResult(operator/, -34, -17, 2); + expectOperationValueResult(exprDiv, -30, -10, 3); + expectOperationValueResult(exprDiv, -34, -17, 2); // Test both positive values. - expectOperationValueResult(operator/, 30, 10, 3); - expectOperationValueResult(operator/, 34, 17, 2); - expectOperationValueResult(operator/, 0, 10, 0); + expectOperationValueResult(exprDiv, 30, 10, 3); + expectOperationValueResult(exprDiv, 34, 17, 2); + expectOperationValueResult(exprDiv, 0, 10, 0); // Test divide by zero. - expectOperationValueResult(operator/, -10, 0); - expectOperationValueResult(operator/, 10, 0); - expectOperationValueResult(operator/, 0, 0); - - // Test negative result that underflows. - expectOperationValueResult(operator/, MaxUint64, -1); - expectOperationValueResult(operator/, AbsoluteMaxInt64 + 2, -1); -} - -TEST_F(FileCheckTest, ExpressionValueEquality) { - // Test negative and positive value. - EXPECT_FALSE(ExpressionValue(5) == ExpressionValue(-3)); - EXPECT_TRUE(ExpressionValue(5) != ExpressionValue(-3)); - EXPECT_FALSE(ExpressionValue(-2) == ExpressionValue(6)); - EXPECT_TRUE(ExpressionValue(-2) != ExpressionValue(6)); - EXPECT_FALSE(ExpressionValue(-7) == ExpressionValue(7)); - EXPECT_TRUE(ExpressionValue(-7) != ExpressionValue(7)); - EXPECT_FALSE(ExpressionValue(4) == ExpressionValue(-4)); - EXPECT_TRUE(ExpressionValue(4) != ExpressionValue(-4)); - EXPECT_FALSE(ExpressionValue(MaxUint64) == ExpressionValue(-1)); - EXPECT_TRUE(ExpressionValue(MaxUint64) != ExpressionValue(-1)); - - // Test both negative values. - EXPECT_FALSE(ExpressionValue(-2) == ExpressionValue(-7)); - EXPECT_TRUE(ExpressionValue(-2) != ExpressionValue(-7)); - EXPECT_TRUE(ExpressionValue(-3) == ExpressionValue(-3)); - EXPECT_FALSE(ExpressionValue(-3) != ExpressionValue(-3)); - EXPECT_FALSE(ExpressionValue(MinInt64) == ExpressionValue(-1)); - EXPECT_TRUE(ExpressionValue(MinInt64) != ExpressionValue(-1)); - EXPECT_FALSE(ExpressionValue(MinInt64) == ExpressionValue(-0)); - EXPECT_TRUE(ExpressionValue(MinInt64) != ExpressionValue(-0)); - - // Test both positive values. - EXPECT_FALSE(ExpressionValue(8) == ExpressionValue(9)); - EXPECT_TRUE(ExpressionValue(8) != ExpressionValue(9)); - EXPECT_TRUE(ExpressionValue(1) == ExpressionValue(1)); - EXPECT_FALSE(ExpressionValue(1) != ExpressionValue(1)); - - // Check the signedness of zero doesn't affect equality. - EXPECT_TRUE(ExpressionValue(0) == ExpressionValue(0)); - EXPECT_FALSE(ExpressionValue(0) != ExpressionValue(0)); - EXPECT_TRUE(ExpressionValue(0) == ExpressionValue(-0)); - EXPECT_FALSE(ExpressionValue(0) != ExpressionValue(-0)); - EXPECT_TRUE(ExpressionValue(-0) == ExpressionValue(0)); - EXPECT_FALSE(ExpressionValue(-0) != ExpressionValue(0)); - EXPECT_TRUE(ExpressionValue(-0) == ExpressionValue(-0)); - EXPECT_FALSE(ExpressionValue(-0) != ExpressionValue(-0)); + expectOperationValueResult(exprDiv, -10, 0); + expectOperationValueResult(exprDiv, 10, 0); + expectOperationValueResult(exprDiv, 0, 0); + + // Test negative result not representable in int64_t. + expectOperationValueResult(exprDiv, MaxUint64, -1, "-18446744073709551615"); + expectOperationValueResult(exprDiv, AbsoluteMaxInt64 + 2, -1, + "-9223372036854775809"); } TEST_F(FileCheckTest, Literal) { SourceMgr SM; // Eval returns the literal's value. - ExpressionLiteral Ten(bufferize(SM, "10"), 10u); - Expected Value = Ten.eval(); + ExpressionLiteral Ten(bufferize(SM, "10"), APInt(64, 10u)); + Expected Value = Ten.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(10, cantFail(Value->getSignedValue())); + EXPECT_EQ(10, Value->getSExtValue()); 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); + ExpressionLiteral Min(bufferize(SM, std::to_string(MinInt64)), + APInt(64, MinInt64, /*IsSigned=*/true)); Value = Min.eval(); ASSERT_TRUE(bool(Value)); - EXPECT_EQ(MinInt64, cantFail(Value->getSignedValue())); + EXPECT_EQ(MinInt64, Value->getSExtValue()); // Max value can be correctly represented. - ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64); + ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), + APInt(64, MaxUint64)); Value = Max.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(MaxUint64, cantFail(Value->getUnsignedValue())); + EXPECT_EQ(MaxUint64, Value->getZExtValue()); } TEST_F(FileCheckTest, Expression) { SourceMgr SM; std::unique_ptr Ten = - std::make_unique(bufferize(SM, "10"), 10u); + std::make_unique(bufferize(SM, "10"), APInt(64, 10u)); ExpressionLiteral *TenPtr = Ten.get(); Expression Expr(std::move(Ten), ExpressionFormat(ExpressionFormat::Kind::HexLower)); @@ -741,33 +615,33 @@ 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()); // Defined variable without string: only getValue and eval return value set. - FooVar.setValue(ExpressionValue(42u)); - std::optional Value = FooVar.getValue(); + FooVar.setValue(APInt(64, 42u)); + std::optional Value = FooVar.getValue(); ASSERT_TRUE(Value); - EXPECT_EQ(42, cantFail(Value->getSignedValue())); + EXPECT_EQ(42u, Value->getZExtValue()); EXPECT_FALSE(FooVar.getStringValue()); EvalResult = FooVarUse.eval(); ASSERT_THAT_EXPECTED(EvalResult, Succeeded()); - EXPECT_EQ(42, cantFail(EvalResult->getSignedValue())); + EXPECT_EQ(42u, EvalResult->getZExtValue()); // Defined variable with string: getValue, eval, and getStringValue return // value set. StringRef StringValue = "925"; - FooVar.setValue(ExpressionValue(925u), StringValue); + FooVar.setValue(APInt(64, 925u), StringValue); Value = FooVar.getValue(); ASSERT_TRUE(Value); - EXPECT_EQ(925, cantFail(Value->getSignedValue())); + EXPECT_EQ(925u, Value->getZExtValue()); // getStringValue should return the same memory not just the same characters. EXPECT_EQ(StringValue.begin(), FooVar.getStringValue()->begin()); EXPECT_EQ(StringValue.end(), FooVar.getStringValue()->end()); EvalResult = FooVarUse.eval(); ASSERT_THAT_EXPECTED(EvalResult, Succeeded()); - EXPECT_EQ(925, cantFail(EvalResult->getSignedValue())); - EXPECT_EQ(925, cantFail(EvalResult->getSignedValue())); + EXPECT_EQ(925u, EvalResult->getZExtValue()); + EXPECT_EQ(925u, EvalResult->getZExtValue()); // Clearing variable: getValue and eval fail. Error returned by eval holds // the name of the cleared variable. @@ -785,28 +659,44 @@ StringRef FooStr = ExprStr.take_front(3); NumericVariable FooVar(FooStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - FooVar.setValue(ExpressionValue(42u)); + FooVar.setValue(APInt(64, 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(ExpressionValue(18u)); + BarVar.setValue(APInt(64, 18u)); std::unique_ptr BarVarUse = std::make_unique(BarStr, &BarVar); - binop_eval_t doAdd = operator+; + binop_eval_t doAdd = exprAdd; 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(); + // Defined variables with same bitwidth and no overflow: eval returns right + // value; implicit format is as expected. + Expected Value = Binop.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); - EXPECT_EQ(60, cantFail(Value->getSignedValue())); + EXPECT_EQ(60u, Value->getZExtValue()); Expected ImplicitFormat = Binop.getImplicitFormat(SM); ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); + // Defined variables with different bitwidth and no overflow: eval succeeds + // and return the right value. + BarVar.setValue(APInt(32, 18u)); + Value = Binop.eval(); + ASSERT_THAT_EXPECTED(Value, Succeeded()); + EXPECT_EQ(60u, Value->getZExtValue()); + + // Defined variables with same bitwidth and wider result (i.e. overflow): + // eval succeeds and return the right value in a wider APInt. + BarVar.setValue(APInt(64, AbsoluteMaxInt64)); + Value = Binop.eval(); + ASSERT_THAT_EXPECTED(Value, Succeeded()); + EXPECT_EQ(128u, Value->getBitWidth()); + EXPECT_EQ(APInt(128, AbsoluteMaxInt64 + FooVar.getValue()->getZExtValue()), + *Value); + // 1 undefined variable: eval fails, error contains name of undefined // variable. FooVar.clearValue(); @@ -825,7 +715,7 @@ StringRef EighteenStr = ExprStr.take_back(2); FooVarUse = std::make_unique(FooStr, &FooVar); std::unique_ptr Eighteen = - std::make_unique(EighteenStr, 18u); + std::make_unique(EighteenStr, APInt(64, 18u)); Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse), std::move(Eighteen)); ImplicitFormat = Binop.getImplicitFormat(SM); @@ -835,7 +725,7 @@ FooStr = ExprStr.take_back(3); EighteenStr = ExprStr.take_front(2); FooVarUse = std::make_unique(FooStr, &FooVar); - Eighteen = std::make_unique(EighteenStr, 18u); + Eighteen = std::make_unique(EighteenStr, APInt(64, 18u)); Binop = BinaryOperation(ExprStr, doAdd, std::move(Eighteen), std::move(FooVarUse)); ImplicitFormat = Binop.getImplicitFormat(SM); @@ -1367,7 +1257,7 @@ // Check a substitution error is diagnosed. ASSERT_FALSE(Tester.parsePattern("[[#%u, -1]]")); expectDiagnosticError( - "unable to substitute variable or numeric expression: overflow error", + "unable to substitute variable or numeric expression: underflow error", Tester.match("").takeError()); // Check matching an empty expression only matches a number. @@ -1561,7 +1451,7 @@ // substituted for the variable's value. NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - NVar.setValue(ExpressionValue(10u)); + NVar.setValue(APInt(64, 10u)); auto NVarUse = std::make_unique("N", &NVar); auto ExpressionN = std::make_unique( std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper)); @@ -1671,24 +1561,23 @@ 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(cantFail(ExpressionVal->getSignedValue()), 18); + EXPECT_EQ(ExpressionVal->getSExtValue(), 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(cantFail(ExpressionVal->getSignedValue()), 20); + EXPECT_EQ(ExpressionVal->getSExtValue(), 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(cantFail(ExpressionVal->getSignedValue()), 12); + EXPECT_EQ(ExpressionVal->getSExtValue(), 12); ASSERT_THAT_EXPECTED(EmptyVar, Succeeded()); EXPECT_EQ(*EmptyVar, ""); expectUndefErrors({std::string(UnknownVarStr)}, UnknownVar.takeError()); @@ -1738,7 +1627,7 @@ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); - EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36); + EXPECT_EQ(ExpressionVal->getSExtValue(), 36); // Clear local variables and check global variables remain defined. Cxt.clearLocalVars(); @@ -1750,7 +1639,7 @@ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded()); ExpressionVal = (*ExpressionPointer)->getAST()->eval(); ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded()); - EXPECT_EQ(cantFail(ExpressionVal->getSignedValue()), 36); + EXPECT_EQ(ExpressionVal->getSExtValue(), 36); } TEST_F(FileCheckTest, CapturedVarDiags) {