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 @@ -593,8 +593,8 @@ * ``%`` is an optional scanf-style matching format specifier to indicate what number format to match (e.g. hex number). Currently accepted - format specifier are ``%u``, ``%x`` and ``%X``. If absent, the format - specifier defaults to ``%u``. + format specifier 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. 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 @@ -29,6 +30,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: @@ -40,43 +43,146 @@ } Expected -ExpressionFormat::getMatchingString(uint64_t IntegerValue) const { +ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const { + Error ConvError = Value == Kind::Signed ? IntegerValue.convertSigned() + : IntegerValue.convertUnsigned(); + + // Conversion error (e.g. negative value converted to unsigned). + if (ConvError) + return std::move(ConvError); + switch (Value) { case Kind::Unsigned: - return utostr(IntegerValue); + return utostr(IntegerValue.getUnsignedValue()); + case Kind::Signed: + return itostr(IntegerValue.getSignedValue()); case Kind::HexUpper: - return utohexstr(IntegerValue, /*LowerCase=*/false); + return utohexstr(IntegerValue.getUnsignedValue(), /*LowerCase=*/false); case Kind::HexLower: - return utohexstr(IntegerValue, /*LowerCase=*/true); + return utohexstr(IntegerValue.getUnsignedValue(), /*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 Hex = Value == Kind::HexUpper || Value == Kind::HexLower; - uint64_t IntegerValue; - if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue)) - return FileCheckErrorDiagnostic::get(SM, StrVal, - "Unable to represent numeric value"); + if (Value == Kind::Signed) { + int64_t SignedValue; + + if (StrVal.getAsInteger(10, SignedValue)) + return FileCheckErrorDiagnostic::get(SM, StrVal, + "Unable to represent numeric value"); + + return ExpressionValue(SignedValue); + } else { + bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; + uint64_t UnsignedValue; + if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue)) + return FileCheckErrorDiagnostic::get(SM, StrVal, + "Unable to represent numeric value"); + + return ExpressionValue(UnsignedValue); + } +} + +bool ExpressionValue::operator==(const ExpressionValue &other) { + if (Signed) + return SignedValue == other.SignedValue; + else + return UnsignedValue == other.UnsignedValue; +} + +Error ExpressionValue::convertSigned() { + if (Signed) + return Error::success(); + + if (UnsignedValue > std::numeric_limits::max()) + return make_error(ValOverflow); - return IntegerValue; + SignedValue = UnsignedValue; + Signed = true; + return Error::success(); } -Expected FileCheckNumericVariableUse::eval() const { - Optional Value = NumericVariable->getValue(); +Error ExpressionValue::convertUnsigned() { + if (!Signed) + return Error::success(); + + if (SignedValue < 0) + return make_error(ValOverflow); + + UnsignedValue = SignedValue; + Signed = false; + return Error::success(); +} + +Expected llvm::operator+(const ExpressionValue &Op1, + const ExpressionValue &Op2) { + // Operands must have same sign. + if (Op1.Signed != Op2.Signed) + return make_error(ValPromotion); + + if (Op1.Signed) { + int64_t Val1 = Op1.SignedValue; + int64_t Val2 = Op2.SignedValue; + + Optional result = checkedAdd(Val1, Val2); + if (!result) + return make_error(ValOverflow); + + return ExpressionValue(*result); + } else { + uint64_t Val1 = Op1.UnsignedValue; + uint64_t Val2 = Op2.UnsignedValue; + + Optional result = checkedAddUnsigned(Val1, Val2); + if (!result) + return make_error(ValOverflow); + + return ExpressionValue(*result); + } +} + +Expected llvm::operator-(const ExpressionValue &Op1, + const ExpressionValue &Op2) { + // Operands must have same sign. + if (Op1.Signed != Op2.Signed) + return make_error(ValPromotion); + + if (Op1.Signed) { + int64_t Val1 = Op1.SignedValue; + int64_t Val2 = Op2.SignedValue; + + Optional result = checkedSub(Val1, Val2); + if (!result) + return make_error(ValOverflow); + + return ExpressionValue(*result); + } else { + uint64_t Val1 = Op1.UnsignedValue; + uint64_t Val2 = Op2.UnsignedValue; + + // Op1 < Op2. + if (Val1 < Val2) + return make_error(ValOverflow); + return ExpressionValue(Val1 - Val2); + } +} + +Expected FileCheckNumericVariableUse::eval() const { + Optional Value = NumericVariable->getValue(); if (Value) return *Value; return make_error(Name); } -Expected FileCheckASTBinop::eval() const { - Expected LeftOp = LeftOperand->eval(); - Expected RightOp = RightOperand->eval(); +Expected FileCheckASTBinop::eval() const { + Expected LeftOp = LeftOperand->eval(); + Expected RightOp = RightOperand->eval(); // Bubble up any error (e.g. undefined variables) in the recursive // evaluation. @@ -89,7 +195,19 @@ return std::move(Err); } - return EvalBinop(*LeftOp, *RightOp); + // Integer promotion. + ExpressionValue LeftValue = *LeftOp; + ExpressionValue RightValue = *RightOp; + if (!LeftValue.isSigned() || !RightValue.isSigned()) { + Error ConvError = LeftValue.convertUnsigned(); + if (ConvError) + return std::move(ConvError); + ConvError = RightValue.convertUnsigned(); + if (ConvError) + return std::move(ConvError); + } + + return EvalBinop(LeftValue, RightValue); } ExpressionFormat FileCheckASTBinop::getImplicitFormat() const { @@ -108,7 +226,7 @@ Expected FileCheckNumericSubstitution::getResult() const { assert(Expression->getAST() != nullptr && "Substituting empty expression"); - Expected EvaluatedValue = Expression->getAST()->eval(); + Expected EvaluatedValue = Expression->getAST()->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); ExpressionFormat Format = Expression->getFormat(); @@ -166,6 +284,7 @@ return C; } +char ValueError::ID = 0; char FileCheckUndefVarError::ID = 0; char FileCheckErrorDiagnostic::ID = 0; char FileCheckNotFoundError::ID = 0; @@ -267,23 +386,21 @@ } // Otherwise, parse it as a literal. - uint64_t LiteralValue; + int64_t SignedLiteralValue; + uint64_t UnsignedLiteralValue; + StringRef SaveExpr = Expr; + // Accept both signed and unsigned literal. + if (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue)) + return std::make_unique(SignedLiteralValue); + Expr = SaveExpr; if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, - LiteralValue)) - return std::make_unique(LiteralValue); + UnsignedLiteralValue)) + return std::make_unique(UnsignedLiteralValue); return FileCheckErrorDiagnostic::get(SM, Expr, "invalid operand format '" + Expr + "'"); } -static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp + RightOp; -} - -static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp - RightOp; -} - Expected> FileCheckPattern::parseBinop( StringRef &Expr, std::unique_ptr LeftOp, bool IsLegacyLineExpr, Optional LineNumber, @@ -299,10 +416,10 @@ binop_eval_t EvalBinop; switch (Operator) { case '+': - EvalBinop = add; + EvalBinop = operator+; break; case '-': - EvalBinop = sub; + EvalBinop = operator-; break; default: return FileCheckErrorDiagnostic::get( @@ -353,6 +470,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; @@ -760,7 +880,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 @@ -769,8 +889,22 @@ 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) { + Error Err = handleErrors(Value.takeError(), [&](const ValueError &E) { + switch (E.getType()) { + case ValOverflow: + case ValPromotion: + return FileCheckErrorDiagnostic::get( + SM, Substitution->getFromString(), + Twine("Unable to substitute variable or numeric " + "expression: ") + + E.getMsg()); + default: + llvm_unreachable("Unexpected value error"); + } + }); + return std::move(Err); + } // Plop it into the regex at the adjusted offset. TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset, @@ -811,7 +945,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); @@ -859,6 +994,15 @@ [](const FileCheckNotFoundError &E) {}, // Handled in PrintNoMatch(). [](const FileCheckErrorDiagnostic &E) {}, + [](const ValueError &E) { + switch (E.getType()) { + case ValOverflow: + case ValPromotion: + case ValStringConversion: + // handled in match() + break; + } + }, [&](const FileCheckUndefVarError &E) { if (!UndefSeen) { OS << "uses undefined variable(s):"; @@ -1969,7 +2113,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 @@ -30,6 +30,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. @@ -43,6 +45,8 @@ Conflict, /// Value is an unsigned integer and should be printed as a decimal number. Unsigned, + /// Value is an unsigned integer and should be printed as a decimal number. + Signed, /// Value should be printed as an uppercase hex number. HexUpper, /// Value should be printed as a lowercase hex number. @@ -79,15 +83,115 @@ 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 or Conflict. - Expected getMatchingString(uint64_t Value) const; + /// by this instance, or an error if conversion to this format failed or if + /// the format is NoFormat or Conflict. + 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; +}; + +enum ValueErrorType { ValOverflow, ValPromotion, ValStringConversion }; + +/// Class to represent an error when manipulating a value. Possible value +/// errors are overflow, promotion and string conversion errors. +class ValueError : public ErrorInfo { +private: + /// Type of value error. + enum ValueErrorType Type; + +public: + static char ID; + + ValueError(ValueErrorType Type) : Type(Type) {} + + ValueErrorType getType() const { return Type; } + + /// \returns string describing this value error. + const char *getMsg() const { + switch (Type) { + case ValOverflow: + return "overflow"; + case ValPromotion: + return "promotion"; + case ValStringConversion: + return "string conversion"; + } + llvm_unreachable("Unknown value error type"); + return nullptr; + } + + std::error_code convertToErrorCode() const override { + return inconvertibleErrorCode(); + } + + /// Print type of value conversion that failed. + void log(raw_ostream &OS) const override { OS << Twine(getMsg()) + " error"; } +}; + +Expected operator+(const ExpressionValue &lhs, + const ExpressionValue &rhs); +Expected operator-(const ExpressionValue &lhs, + const ExpressionValue &rhs); + +/// Class representing a numeric value. +class ExpressionValue { +private: + union { + int64_t SignedValue; + uint64_t UnsignedValue; + }; + + /// Whether value is signed (and thus is stored in SignedValue) or not (in + /// which case it is stored in UnsignedValue). + bool Signed; + +public: + /// Constructor for a signed value. + explicit ExpressionValue(int64_t Val) : SignedValue(Val), Signed(true) {} + + /// Constructor for an unsigned value. + explicit ExpressionValue(uint64_t Val) : UnsignedValue(Val), Signed(false) {} + + /// Define equality to be true only if both values are valid and they have + /// the same signedness and corresponding value. + bool operator==(const ExpressionValue &other); + bool operator!=(const ExpressionValue &other) { return !(*this == other); } + + bool isSigned() const { return Signed; } + + /// \returns the signed value. Must only be called if value is signed in the + /// first place. + int64_t getSignedValue() const { + assert(Signed); + return SignedValue; + } + + /// \returns the unsigned value. Must only be called if value is unsigned in + /// the first place. + uint64_t getUnsignedValue() const { + assert(!Signed); + return UnsignedValue; + } + + /// Converts this value to a signed value. \returns an error if not possible + /// (original value was not within range for a signed integer). + Error convertSigned(); + + /// Converts this value to an unsigned value. \returns an error if not + /// possible (original value was not within range for an unsigned integer). + Error convertUnsigned(); + + /// Performs operation and \returns its result or an error in case of + /// overflow. + friend Expected operator+(const ExpressionValue &lhs, + const ExpressionValue &rhs); + friend Expected operator-(const ExpressionValue &lhs, + const ExpressionValue &rhs); }; /// Base class representing the AST of a given expression. @@ -97,7 +201,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, FormatConflict if /// implicit formats of the AST's components conflict, or NoFormat if the AST @@ -111,14 +215,17 @@ class FileCheckExpressionLiteral : public FileCheckExpressionAST { private: /// Actual value of the literal. - uint64_t Value; + ExpressionValue Value; public: - /// Constructs a literal with the specified value. - FileCheckExpressionLiteral(uint64_t Val) : Value(Val) {} + /// Constructor for a signed literal. + explicit FileCheckExpressionLiteral(int64_t Val) : Value(Val) {} + + /// Constructor for an unsigned literal. + explicit FileCheckExpressionLiteral(uint64_t Val) : 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 @@ -178,8 +285,8 @@ /// format. ExpressionFormat ImplicitFormat; - /// Value of numeric variable, if defined, or None otherwise. - Optional Value; + /// Value of numeric variable. + 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 @@ -203,10 +310,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. @@ -233,7 +340,7 @@ : Name(Name), NumericVariable(NumericVariable) {} /// \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. ExpressionFormat getImplicitFormat() const override { @@ -242,7 +349,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 FileCheckASTBinop : public FileCheckExpressionAST { @@ -269,7 +377,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 format conflict if /// the implicit formats of the AST's components conflict, or no format if 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 @@ -27,18 +27,20 @@ ; Numeric variable definition with explicit matching format. DEF FMT +-30 c D CHECK-LABEL: DEF FMT -CHECK-NEXT: [[#%x,VAR2:]] -CHECK-NEXT: [[#%X,VAR3:]] +CHECK-NEXT: [[#%d,VAR2:]] +CHECK-NEXT: [[#%x,VAR3:]] +CHECK-NEXT: [[#%X,VAR4:]] ; Numeric variable definition with explicit matching format with different ; spacing. DEF FMT SPC -c +-30 CHECK-LABEL: DEF FMT SPC -CHECK-NEXT: [[# %x , VAR2a : ]] +CHECK-NEXT: [[# %d , VAR2a : ]] ; Numeric variable definition with unsupported matching format. RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC1 --input-file %s %s 2>&1 \ @@ -65,38 +67,48 @@ 11 12 10 +-30 +-29 +-31 c d b 1a +1a D E C 1B +1B 11 11 11 -c -c -c -c -c +-30 +-30 +-30 +-30 +-30 CHECK-LABEL: USE DEF FMT IMPL MATCH CHECK-NEXT: [[#%u,VAR1]] CHECK-NEXT: [[#%u,VAR1+1]] CHECK-NEXT: [[#%u,VAR1-1]] -CHECK-NEXT: [[#%x,VAR2]] -CHECK-NEXT: [[#%x,VAR2+1]] -CHECK-NEXT: [[#%x,VAR2-1]] -CHECK-NEXT: [[#%x,VAR2+14]] -CHECK-NEXT: [[#%X,VAR3]] -CHECK-NEXT: [[#%X,VAR3+1]] -CHECK-NEXT: [[#%X,VAR3-1]] -CHECK-NEXT: [[#%X,VAR3+14]] +CHECK-NEXT: [[#%d,VAR2]] +CHECK-NEXT: [[#%d,VAR2+1]] +CHECK-NEXT: [[#%d,VAR2-1]] +CHECK-NEXT: [[#%x,VAR3]] +CHECK-NEXT: [[#%x,VAR3+1]] +CHECK-NEXT: [[#%x,VAR3-1]] +CHECK-NEXT: [[#%x,VAR3+0xe]] +CHECK-NEXT: [[#%x,VAR3+0xE]] +CHECK-NEXT: [[#%X,VAR4]] +CHECK-NEXT: [[#%X,VAR4+1]] +CHECK-NEXT: [[#%X,VAR4-1]] +CHECK-NEXT: [[#%X,VAR4+0xe]] +CHECK-NEXT: [[#%X,VAR4+0xE]] CHECK-NEXT: [[#%u,VAR1a]] CHECK-NEXT: [[#%u,VAR1b]] CHECK-NEXT: [[#%u,VAR1c]] -CHECK-NEXT: [[#%x,VAR2a]] +CHECK-NEXT: [[#%d,VAR2a]] ; Numeric expressions in explicit matching format and default matching rule using ; variables defined on other lines with different spacing. @@ -139,14 +151,19 @@ 11 12 10 +-30 +-29 +-31 c d b 1a +1a D E C 1B +1B CHECK-LABEL: USE IMPL FMT IMPL MATCH CHECK-NEXT: [[#VAR1]] CHECK-NEXT: [[#VAR1+1]] @@ -154,11 +171,16 @@ CHECK-NEXT: [[#VAR2]] CHECK-NEXT: [[#VAR2+1]] CHECK-NEXT: [[#VAR2-1]] -CHECK-NEXT: [[#VAR2+14]] CHECK-NEXT: [[#VAR3]] CHECK-NEXT: [[#VAR3+1]] CHECK-NEXT: [[#VAR3-1]] -CHECK-NEXT: [[#VAR3+14]] +CHECK-NEXT: [[#VAR3+0xe]] +CHECK-NEXT: [[#VAR3+0xE]] +CHECK-NEXT: [[#VAR4]] +CHECK-NEXT: [[#VAR4+1]] +CHECK-NEXT: [[#VAR4-1]] +CHECK-NEXT: [[#VAR4+0xe]] +CHECK-NEXT: [[#VAR4+0xE]] ; Numeric expressions using variables defined on other lines and an immediate ; interpreted as an unsigned value. @@ -174,12 +196,16 @@ b B 12 +12 +13 13 CHECK-LABEL: USE CONV FMT IMPL MATCH CHECK-NEXT: [[# %x, VAR1]] CHECK-NEXT: [[# %X, VAR1]] -CHECK-NEXT: [[# %u, VAR2]] CHECK-NEXT: [[# %u, VAR3]] +CHECK-NEXT: [[# %d, VAR3]] +CHECK-NEXT: [[# %u, VAR4]] +CHECK-NEXT: [[# %d, VAR4]] ; Conflicting implicit format. RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT --input-file %s %s 2>&1 \ @@ -197,15 +223,15 @@ VAR USE IMPL OVERRIDE FMT CONFLICT 23 CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT -CHECK-NEXT: [[# %u, VAR1 + VAR2]] +CHECK-NEXT: [[# %u, VAR1 + VAR3]] ; Numeric expressions using more than one variable defined on other lines. USE MULTI VAR 31 42 CHECK-LABEL: USE MULTI VAR -CHECK-NEXT: [[#VAR4:]] -CHECK-NEXT: [[#VAR1+VAR4]] +CHECK-NEXT: [[#VAR5:]] +CHECK-NEXT: [[#VAR1+VAR5]] ; Numeric expression using a variable defined from a numeric expression. DEF EXPR GOOD MATCH @@ -370,4 +396,28 @@ REDEF-NEW-FMT-NEXT: [[#%X,VAR1:]] REDEF-NEW-FMT-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: format different from previous variable definition REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,VAR1:\]\]}} -REDEF-NEW-FMT-MSG-NEXT: {{^ \^$}} +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 @@ -29,58 +29,72 @@ SourceMgr SM; ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned); - Expected MatchingString = UnsignedFormat.getMatchingString(42); + Expected MatchingString = + UnsignedFormat.getMatchingString(ExpressionValue((uint64_t)42)); EXPECT_TRUE(bool(MatchingString)); EXPECT_EQ(*MatchingString, "42"); StringRef ThreeTwo = bufferize(SM, "32"); - Expected Val = UnsignedFormat.valueFromStringRepr(ThreeTwo, SM); + Expected Val = + UnsignedFormat.valueFromStringRepr(ThreeTwo, SM); ASSERT_TRUE(bool(Val)); - EXPECT_EQ(*Val, 32U); + EXPECT_FALSE(Val->isSigned()); + EXPECT_EQ(Val->getUnsignedValue(), 32U); StringRef ALower = bufferize(SM, "a"); EXPECT_TRUE( errorToBool(UnsignedFormat.valueFromStringRepr(ALower, SM).takeError())); ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower); - MatchingString = HexLowerFormat.getMatchingString(11); + MatchingString = + HexLowerFormat.getMatchingString(ExpressionValue((uint64_t)11)); EXPECT_TRUE(bool(MatchingString)); EXPECT_EQ(*MatchingString, "b"); Val = HexLowerFormat.valueFromStringRepr(ALower, SM); ASSERT_TRUE(bool(Val)); - EXPECT_EQ(*Val, 10U); + EXPECT_FALSE(Val->isSigned()); + EXPECT_EQ(Val->getUnsignedValue(), 10U); StringRef GLower = bufferize(SM, "g"); EXPECT_TRUE( errorToBool(HexLowerFormat.valueFromStringRepr(GLower, SM).takeError())); ExpressionFormat HexUpperFormat(ExpressionFormat::Kind::HexUpper); - MatchingString = HexUpperFormat.getMatchingString(12); + MatchingString = + HexUpperFormat.getMatchingString(ExpressionValue((uint64_t)12)); EXPECT_TRUE(bool(MatchingString)); EXPECT_EQ(*MatchingString, "C"); StringRef DUpper = bufferize(SM, "D"); Val = HexUpperFormat.valueFromStringRepr(DUpper, SM); ASSERT_TRUE(bool(Val)); - EXPECT_EQ(*Val, 13U); + EXPECT_FALSE(Val->isSigned()); + EXPECT_EQ(Val->getUnsignedValue(), 13U); StringRef HUpper = bufferize(SM, "H"); EXPECT_TRUE( errorToBool(HexUpperFormat.valueFromStringRepr(HUpper, SM).takeError())); ExpressionFormat NoneFormat; - ASSERT_TRUE(errorToBool(NoneFormat.getMatchingString(18).takeError())); + ExpressionValue Eighteen((uint64_t)18); + ASSERT_TRUE(errorToBool(NoneFormat.getMatchingString(Eighteen).takeError())); ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); - ASSERT_TRUE(errorToBool(ConflictFormat.getMatchingString(18).takeError())); + ASSERT_TRUE(errorToBool(ConflictFormat.getMatchingString(Eighteen).takeError())); } TEST_F(FileCheckTest, Literal) { // Eval returns the literal's value. - FileCheckExpressionLiteral Ten(10); - Expected Value = Ten.eval(); + FileCheckExpressionLiteral Ten((uint64_t)10); + Expected Value = Ten.eval(); ASSERT_TRUE(bool(Value)); - EXPECT_EQ(10U, *Value); + EXPECT_EQ(10U, Value->getUnsignedValue()); + + // Min value can be correctly represented. + FileCheckExpressionLiteral Min(std::numeric_limits::min()); + Value = Min.eval(); + ASSERT_TRUE(bool(Value)); + EXPECT_EQ(std::numeric_limits::min(), Value->getSignedValue()); // Max value can be correctly represented. FileCheckExpressionLiteral Max(std::numeric_limits::max()); Value = Max.eval(); ASSERT_TRUE(bool(Value)); - EXPECT_EQ(std::numeric_limits::max(), *Value); + EXPECT_EQ(std::numeric_limits::max(), Value->getUnsignedValue()); } static std::string toString(const std::unordered_set &Set) { @@ -109,7 +123,20 @@ expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err)); } -uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } +Expected doAdd(const ExpressionValue &Op1, + const ExpressionValue &Op2) { + if (Op1.isSigned()) { + int64_t Val1 = Op1.getSignedValue(); + int64_t Val2 = Op2.getSignedValue(); + + return ExpressionValue(Val1 + Val2); + } else { + uint64_t Val1 = Op1.getUnsignedValue(); + uint64_t Val2 = Op2.getUnsignedValue(); + + return ExpressionValue(Val1 + Val2); + } +} TEST_F(FileCheckTest, NumericVariable) { // Undefined variable: getValue and eval fail, error returned by eval holds @@ -119,19 +146,19 @@ EXPECT_EQ("FOO", FooVar.getName()); FileCheckNumericVariableUse FooVarUse("FOO", &FooVar); EXPECT_FALSE(FooVar.getValue()); - Expected EvalResult = FooVarUse.eval(); + Expected EvalResult = FooVarUse.eval(); ASSERT_FALSE(EvalResult); expectUndefError("FOO", EvalResult.takeError()); - FooVar.setValue(42); + FooVar.setValue(ExpressionValue((uint64_t)42)); // Defined variable: getValue and eval return value set. - Optional Value = FooVar.getValue(); + Optional Value = FooVar.getValue(); ASSERT_TRUE(bool(Value)); - EXPECT_EQ(42U, *Value); + EXPECT_EQ(42U, Value->getUnsignedValue()); EvalResult = FooVarUse.eval(); ASSERT_TRUE(bool(EvalResult)); - EXPECT_EQ(42U, *EvalResult); + EXPECT_EQ(42U, EvalResult->getUnsignedValue()); // Clearing variable: getValue and eval fail. Error returned by eval holds // the name of the cleared variable. @@ -145,20 +172,20 @@ TEST_F(FileCheckTest, Binop) { FileCheckNumericVariable FooVar( "FOO", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - FooVar.setValue(42); + FooVar.setValue(ExpressionValue((uint64_t)42)); std::unique_ptr FooVarUse = std::make_unique("FOO", &FooVar); FileCheckNumericVariable BarVar( "BAR", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2); - BarVar.setValue(18); + BarVar.setValue(ExpressionValue((uint64_t)18)); std::unique_ptr BarVarUse = std::make_unique("BAR", &BarVar); FileCheckASTBinop Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse)); // Defined variable: eval returns right value. - Expected Value = Binop.eval(); + Expected Value = Binop.eval(); ASSERT_TRUE(bool(Value)); - EXPECT_EQ(60U, *Value); + EXPECT_EQ(60U, Value->getUnsignedValue()); // 1 undefined variable: eval fails, error contains name of undefined // variable. @@ -467,8 +494,8 @@ "@LINE", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); FileCheckNumericVariable NVar( "N", ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); - LineVar.setValue(42); - NVar.setValue(10); + LineVar.setValue(ExpressionValue((uint64_t)42)); + NVar.setValue(ExpressionValue((uint64_t)10)); auto LineVarUse = std::make_unique("@LINE", &LineVar); auto NVarUse = std::make_unique("N", &NVar); @@ -582,9 +609,9 @@ Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); ASSERT_TRUE(bool(Expression)); - Expected ExpressionVal = (*Expression)->getAST()->eval(); + Expected ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); - EXPECT_EQ(*ExpressionVal, 18U); + EXPECT_EQ(ExpressionVal->getSignedValue(), 18); Expression = P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false, @@ -592,7 +619,7 @@ ASSERT_TRUE(bool(Expression)); ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); - EXPECT_EQ(*ExpressionVal, 20U); + EXPECT_EQ(ExpressionVal->getSignedValue(), 20); ASSERT_TRUE(bool(EmptyVar)); EXPECT_EQ(*EmptyVar, ""); EXPECT_TRUE(errorToBool(UnknownVar.takeError())); @@ -641,7 +668,7 @@ ASSERT_TRUE(bool(Expression)); ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); - EXPECT_EQ(*ExpressionVal, 36U); + EXPECT_EQ(ExpressionVal->getSignedValue(), 36); // Clear local variables and check global variables remain defined. Cxt.clearLocalVars(); @@ -653,6 +680,6 @@ ASSERT_TRUE(bool(Expression)); ExpressionVal = (*Expression)->getAST()->eval(); ASSERT_TRUE(bool(ExpressionVal)); - EXPECT_EQ(*ExpressionVal, 36U); + EXPECT_EQ(ExpressionVal->getSignedValue(), 36); } } // namespace