diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 | 
| commit | 5ffd83dbcc34f10e07f6d3e968ae6365869615f4 (patch) | |
| tree | 0e9f5cf729dde39f949698fddef45a34e2bc7f44 /contrib/llvm-project/llvm/lib/Support/FileCheck.cpp | |
| parent | 1799696096df87b52968b8996d00c91e0a5de8d9 (diff) | |
| parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) | |
Notes
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Support/FileCheck.cpp')
| -rw-r--r-- | contrib/llvm-project/llvm/lib/Support/FileCheck.cpp | 928 | 
1 files changed, 767 insertions, 161 deletions
diff --git a/contrib/llvm-project/llvm/lib/Support/FileCheck.cpp b/contrib/llvm-project/llvm/lib/Support/FileCheck.cpp index 2261ecc236c2..d0e79c675bcb 100644 --- a/contrib/llvm-project/llvm/lib/Support/FileCheck.cpp +++ b/contrib/llvm-project/llvm/lib/Support/FileCheck.cpp @@ -17,6 +17,7 @@  #include "FileCheckImpl.h"  #include "llvm/ADT/StringSet.h"  #include "llvm/ADT/Twine.h" +#include "llvm/Support/CheckedArithmetic.h"  #include "llvm/Support/FormatVariadic.h"  #include <cstdint>  #include <list> @@ -25,17 +26,301 @@  using namespace llvm; -Expected<uint64_t> NumericVariableUse::eval() const { -  Optional<uint64_t> Value = Variable->getValue(); +StringRef ExpressionFormat::toString() const { +  switch (Value) { +  case Kind::NoFormat: +    return StringRef("<none>"); +  case Kind::Unsigned: +    return StringRef("%u"); +  case Kind::Signed: +    return StringRef("%d"); +  case Kind::HexUpper: +    return StringRef("%X"); +  case Kind::HexLower: +    return StringRef("%x"); +  } +  llvm_unreachable("unknown expression format"); +} + +Expected<StringRef> ExpressionFormat::getWildcardRegex() const { +  switch (Value) { +  case Kind::Unsigned: +    return StringRef("[0-9]+"); +  case Kind::Signed: +    return StringRef("-?[0-9]+"); +  case Kind::HexUpper: +    return StringRef("[0-9A-F]+"); +  case Kind::HexLower: +    return StringRef("[0-9a-f]+"); +  default: +    return createStringError(std::errc::invalid_argument, +                             "trying to match value with invalid format"); +  } +} + +Expected<std::string> +ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const { +  if (Value == Kind::Signed) { +    Expected<int64_t> SignedValue = IntegerValue.getSignedValue(); +    if (!SignedValue) +      return SignedValue.takeError(); +    return itostr(*SignedValue); +  } + +  Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue(); +  if (!UnsignedValue) +    return UnsignedValue.takeError(); +  switch (Value) { +  case Kind::Unsigned: +    return utostr(*UnsignedValue); +  case Kind::HexUpper: +    return utohexstr(*UnsignedValue, /*LowerCase=*/false); +  case Kind::HexLower: +    return utohexstr(*UnsignedValue, /*LowerCase=*/true); +  default: +    return createStringError(std::errc::invalid_argument, +                             "trying to match value with invalid format"); +  } +} + +Expected<ExpressionValue> +ExpressionFormat::valueFromStringRepr(StringRef StrVal, +                                      const SourceMgr &SM) const { +  bool ValueIsSigned = Value == Kind::Signed; +  StringRef OverflowErrorStr = "unable to represent numeric value"; +  if (ValueIsSigned) { +    int64_t SignedValue; + +    if (StrVal.getAsInteger(10, SignedValue)) +      return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); + +    return ExpressionValue(SignedValue); +  } + +  bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; +  uint64_t UnsignedValue; +  if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue)) +    return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); + +  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<int64_t> ExpressionValue::getSignedValue() const { +  if (Negative) +    return getAsSigned(Value); + +  if (Value > (uint64_t)std::numeric_limits<int64_t>::max()) +    return make_error<OverflowError>(); + +  // Value is in the representable range of int64_t so we can use cast. +  return static_cast<int64_t>(Value); +} + +Expected<uint64_t> ExpressionValue::getUnsignedValue() const { +  if (Negative) +    return make_error<OverflowError>(); + +  return Value; +} + +ExpressionValue ExpressionValue::getAbsolute() const { +  if (!Negative) +    return *this; + +  int64_t SignedValue = getAsSigned(Value); +  int64_t MaxInt64 = std::numeric_limits<int64_t>::max(); +  // Absolute value can be represented as int64_t. +  if (SignedValue >= -MaxInt64) +    return ExpressionValue(-getAsSigned(Value)); + +  // -X == -(max int64_t + Rem), negate each component independently. +  SignedValue += MaxInt64; +  uint64_t RemainingValueAbsolute = -SignedValue; +  return ExpressionValue(MaxInt64 + RemainingValueAbsolute); +} + +Expected<ExpressionValue> llvm::operator+(const ExpressionValue &LeftOperand, +                                          const ExpressionValue &RightOperand) { +  if (LeftOperand.isNegative() && RightOperand.isNegative()) { +    int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); +    int64_t RightValue = cantFail(RightOperand.getSignedValue()); +    Optional<int64_t> Result = checkedAdd<int64_t>(LeftValue, RightValue); +    if (!Result) +      return make_error<OverflowError>(); + +    return ExpressionValue(*Result); +  } + +  // (-A) + B == B - A. +  if (LeftOperand.isNegative()) +    return RightOperand - LeftOperand.getAbsolute(); + +  // A + (-B) == A - B. +  if (RightOperand.isNegative()) +    return LeftOperand - RightOperand.getAbsolute(); + +  // Both values are positive at this point. +  uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); +  uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); +  Optional<uint64_t> Result = +      checkedAddUnsigned<uint64_t>(LeftValue, RightValue); +  if (!Result) +    return make_error<OverflowError>(); + +  return ExpressionValue(*Result); +} + +Expected<ExpressionValue> llvm::operator-(const ExpressionValue &LeftOperand, +                                          const ExpressionValue &RightOperand) { +  // Result will be negative and thus might underflow. +  if (LeftOperand.isNegative() && !RightOperand.isNegative()) { +    int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); +    uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); +    // Result <= -1 - (max int64_t) which overflows on 1- and 2-complement. +    if (RightValue > (uint64_t)std::numeric_limits<int64_t>::max()) +      return make_error<OverflowError>(); +    Optional<int64_t> Result = +        checkedSub(LeftValue, static_cast<int64_t>(RightValue)); +    if (!Result) +      return make_error<OverflowError>(); + +    return ExpressionValue(*Result); +  } + +  // (-A) - (-B) == B - A. +  if (LeftOperand.isNegative()) +    return RightOperand.getAbsolute() - LeftOperand.getAbsolute(); + +  // A - (-B) == A + B. +  if (RightOperand.isNegative()) +    return LeftOperand + RightOperand.getAbsolute(); + +  // Both values are positive at this point. +  uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); +  uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); +  if (LeftValue >= RightValue) +    return ExpressionValue(LeftValue - RightValue); +  else { +    uint64_t AbsoluteDifference = RightValue - LeftValue; +    uint64_t MaxInt64 = std::numeric_limits<int64_t>::max(); +    // Value might underflow. +    if (AbsoluteDifference > MaxInt64) { +      AbsoluteDifference -= MaxInt64; +      int64_t Result = -MaxInt64; +      int64_t MinInt64 = std::numeric_limits<int64_t>::min(); +      // Underflow, tested by: +      //   abs(Result + (max int64_t)) > abs((min int64_t) + (max int64_t)) +      if (AbsoluteDifference > static_cast<uint64_t>(-(MinInt64 - Result))) +        return make_error<OverflowError>(); +      Result -= static_cast<int64_t>(AbsoluteDifference); +      return ExpressionValue(Result); +    } + +    return ExpressionValue(-static_cast<int64_t>(AbsoluteDifference)); +  } +} + +Expected<ExpressionValue> 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()); +  Optional<uint64_t> Result = +      checkedMulUnsigned<uint64_t>(LeftValue, RightValue); +  if (!Result) +    return make_error<OverflowError>(); + +  return ExpressionValue(*Result); +} + +Expected<ExpressionValue> 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<OverflowError>(); + +  // 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<ExpressionValue> 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<ExpressionValue> llvm::min(const ExpressionValue &LeftOperand, +                                    const ExpressionValue &RightOperand) { +  if (cantFail(max(LeftOperand, RightOperand)) == LeftOperand) +    return RightOperand; + +  return LeftOperand; +} + +Expected<ExpressionValue> NumericVariableUse::eval() const { +  Optional<ExpressionValue> Value = Variable->getValue();    if (Value)      return *Value; -  return make_error<UndefVarError>(Name); +  return make_error<UndefVarError>(getExpressionStr());  } -Expected<uint64_t> BinaryOperation::eval() const { -  Expected<uint64_t> LeftOp = LeftOperand->eval(); -  Expected<uint64_t> RightOp = RightOperand->eval(); +Expected<ExpressionValue> BinaryOperation::eval() const { +  Expected<ExpressionValue> LeftOp = LeftOperand->eval(); +  Expected<ExpressionValue> RightOp = RightOperand->eval();    // Bubble up any error (e.g. undefined variables) in the recursive    // evaluation. @@ -51,11 +336,42 @@ Expected<uint64_t> BinaryOperation::eval() const {    return EvalBinop(*LeftOp, *RightOp);  } +Expected<ExpressionFormat> +BinaryOperation::getImplicitFormat(const SourceMgr &SM) const { +  Expected<ExpressionFormat> LeftFormat = LeftOperand->getImplicitFormat(SM); +  Expected<ExpressionFormat> RightFormat = RightOperand->getImplicitFormat(SM); +  if (!LeftFormat || !RightFormat) { +    Error Err = Error::success(); +    if (!LeftFormat) +      Err = joinErrors(std::move(Err), LeftFormat.takeError()); +    if (!RightFormat) +      Err = joinErrors(std::move(Err), RightFormat.takeError()); +    return std::move(Err); +  } + +  if (*LeftFormat != ExpressionFormat::Kind::NoFormat && +      *RightFormat != ExpressionFormat::Kind::NoFormat && +      *LeftFormat != *RightFormat) +    return ErrorDiagnostic::get( +        SM, getExpressionStr(), +        "implicit format conflict between '" + LeftOperand->getExpressionStr() + +            "' (" + LeftFormat->toString() + ") and '" + +            RightOperand->getExpressionStr() + "' (" + RightFormat->toString() + +            "), need an explicit format specifier"); + +  return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat +                                                         : *RightFormat; +} +  Expected<std::string> NumericSubstitution::getResult() const { -  Expected<uint64_t> EvaluatedValue = ExpressionASTPointer->eval(); +  assert(ExpressionPointer->getAST() != nullptr && +         "Substituting empty expression"); +  Expected<ExpressionValue> EvaluatedValue = +      ExpressionPointer->getAST()->eval();    if (!EvaluatedValue)      return EvaluatedValue.takeError(); -  return utostr(*EvaluatedValue); +  ExpressionFormat Format = ExpressionPointer->getFormat(); +  return Format.getMatchingString(*EvaluatedValue);  }  Expected<std::string> StringSubstitution::getResult() const { @@ -66,30 +382,27 @@ Expected<std::string> StringSubstitution::getResult() const {    return Regex::escape(*VarVal);  } -bool Pattern::isValidVarNameStart(char C) { return C == '_' || isalpha(C); } +bool Pattern::isValidVarNameStart(char C) { return C == '_' || isAlpha(C); }  Expected<Pattern::VariableProperties>  Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {    if (Str.empty())      return ErrorDiagnostic::get(SM, Str, "empty variable name"); -  bool ParsedOneChar = false; -  unsigned I = 0; +  size_t I = 0;    bool IsPseudo = Str[0] == '@';    // Global vars start with '$'.    if (Str[0] == '$' || IsPseudo)      ++I; -  for (unsigned E = Str.size(); I != E; ++I) { -    if (!ParsedOneChar && !isValidVarNameStart(Str[I])) -      return ErrorDiagnostic::get(SM, Str, "invalid variable name"); +  if (!isValidVarNameStart(Str[I++])) +    return ErrorDiagnostic::get(SM, Str, "invalid variable name"); +  for (size_t E = Str.size(); I != E; ++I)      // Variable names are composed of alphanumeric characters and underscores. -    if (Str[I] != '_' && !isalnum(Str[I])) +    if (Str[I] != '_' && !isAlnum(Str[I]))        break; -    ParsedOneChar = true; -  }    StringRef Name = Str.take_front(I);    Str = Str.substr(I); @@ -107,13 +420,15 @@ static char popFront(StringRef &S) {    return C;  } +char OverflowError::ID = 0;  char UndefVarError::ID = 0;  char ErrorDiagnostic::ID = 0;  char NotFoundError::ID = 0;  Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(      StringRef &Expr, FileCheckPatternContext *Context, -    Optional<size_t> LineNumber, const SourceMgr &SM) { +    Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat, +    const SourceMgr &SM) {    Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);    if (!ParseVarResult)      return ParseVarResult.takeError(); @@ -137,10 +452,14 @@ Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(    NumericVariable *DefinedNumericVariable;    auto VarTableIter = Context->GlobalNumericVariableTable.find(Name); -  if (VarTableIter != Context->GlobalNumericVariableTable.end()) +  if (VarTableIter != Context->GlobalNumericVariableTable.end()) {      DefinedNumericVariable = VarTableIter->second; -  else -    DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber); +    if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat) +      return ErrorDiagnostic::get( +          SM, Expr, "format different from previous variable definition"); +  } else +    DefinedNumericVariable = +        Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);    return DefinedNumericVariable;  } @@ -165,7 +484,8 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(    if (VarTableIter != Context->GlobalNumericVariableTable.end())      NumericVariable = VarTableIter->second;    else { -    NumericVariable = Context->makeNumericVariable(Name); +    NumericVariable = Context->makeNumericVariable( +        Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));      Context->GlobalNumericVariableTable[Name] = NumericVariable;    } @@ -180,16 +500,36 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse(  }  Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand( -    StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber, -    FileCheckPatternContext *Context, const SourceMgr &SM) { +    StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint, +    Optional<size_t> LineNumber, FileCheckPatternContext *Context, +    const SourceMgr &SM) { +  if (Expr.startswith("(")) { +    if (AO != AllowedOperand::Any) +      return ErrorDiagnostic::get( +          SM, Expr, "parenthesized expression not permitted here"); +    return parseParenExpr(Expr, LineNumber, Context, SM); +  } +    if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) {      // Try to parse as a numeric variable use.      Expected<Pattern::VariableProperties> ParseVarResult =          parseVariable(Expr, SM); -    if (ParseVarResult) +    if (ParseVarResult) { +      // Try to parse a function call. +      if (Expr.ltrim(SpaceChars).startswith("(")) { +        if (AO != AllowedOperand::Any) +          return ErrorDiagnostic::get(SM, ParseVarResult->Name, +                                      "unexpected function call"); + +        return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context, +                             SM); +      } +        return parseNumericVariableUse(ParseVarResult->Name,                                       ParseVarResult->IsPseudo, LineNumber,                                       Context, SM); +    } +      if (AO == AllowedOperand::LineVar)        return ParseVarResult.takeError();      // Ignore the error and retry parsing as a literal. @@ -197,41 +537,79 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(    }    // Otherwise, parse it as a literal. -  uint64_t LiteralValue; -  if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue)) -    return std::make_unique<ExpressionLiteral>(LiteralValue); - -  return ErrorDiagnostic::get(SM, Expr, -                              "invalid operand format '" + Expr + "'"); +  int64_t SignedLiteralValue; +  uint64_t UnsignedLiteralValue; +  StringRef SaveExpr = Expr; +  // Accept both signed and unsigned literal, default to signed literal. +  if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, +                           UnsignedLiteralValue)) +    return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()), +                                               UnsignedLiteralValue); +  Expr = SaveExpr; +  if (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue)) +    return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()), +                                               SignedLiteralValue); + +  return ErrorDiagnostic::get( +      SM, Expr, +      Twine("invalid ") + +          (MaybeInvalidConstraint ? "matching constraint or " : "") + +          "operand format");  } -static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { -  return LeftOp + RightOp; -} +Expected<std::unique_ptr<ExpressionAST>> +Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber, +                        FileCheckPatternContext *Context, const SourceMgr &SM) { +  Expr = Expr.ltrim(SpaceChars); +  assert(Expr.startswith("(")); + +  // Parse right operand. +  Expr.consume_front("("); +  Expr = Expr.ltrim(SpaceChars); +  if (Expr.empty()) +    return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); -static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { -  return LeftOp - RightOp; +  // Note: parseNumericOperand handles nested opening parentheses. +  Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand( +      Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber, +      Context, SM); +  Expr = Expr.ltrim(SpaceChars); +  while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) { +    StringRef OrigExpr = Expr; +    SubExprResult = parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false, +                               LineNumber, Context, SM); +    Expr = Expr.ltrim(SpaceChars); +  } +  if (!SubExprResult) +    return SubExprResult; + +  if (!Expr.consume_front(")")) { +    return ErrorDiagnostic::get(SM, Expr, +                                "missing ')' at end of nested expression"); +  } +  return SubExprResult;  }  Expected<std::unique_ptr<ExpressionAST>> -Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp, +Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, +                    std::unique_ptr<ExpressionAST> LeftOp,                      bool IsLegacyLineExpr, Optional<size_t> LineNumber,                      FileCheckPatternContext *Context, const SourceMgr &SM) { -  Expr = Expr.ltrim(SpaceChars); -  if (Expr.empty()) +  RemainingExpr = RemainingExpr.ltrim(SpaceChars); +  if (RemainingExpr.empty())      return std::move(LeftOp);    // Check if this is a supported operation and select a function to perform    // it. -  SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); -  char Operator = popFront(Expr); +  SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data()); +  char Operator = popFront(RemainingExpr);    binop_eval_t EvalBinop;    switch (Operator) {    case '+': -    EvalBinop = add; +    EvalBinop = operator+;      break;    case '-': -    EvalBinop = sub; +    EvalBinop = operator-;      break;    default:      return ErrorDiagnostic::get( @@ -239,29 +617,145 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,    }    // Parse right operand. -  Expr = Expr.ltrim(SpaceChars); -  if (Expr.empty()) -    return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); +  RemainingExpr = RemainingExpr.ltrim(SpaceChars); +  if (RemainingExpr.empty()) +    return ErrorDiagnostic::get(SM, RemainingExpr, +                                "missing operand in expression");    // The second operand in a legacy @LINE expression is always a literal.    AllowedOperand AO = -      IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any; +      IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;    Expected<std::unique_ptr<ExpressionAST>> RightOpResult = -      parseNumericOperand(Expr, AO, LineNumber, Context, SM); +      parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false, +                          LineNumber, Context, SM);    if (!RightOpResult)      return RightOpResult; -  Expr = Expr.ltrim(SpaceChars); -  return std::make_unique<BinaryOperation>(EvalBinop, std::move(LeftOp), +  Expr = Expr.drop_back(RemainingExpr.size()); +  return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp),                                             std::move(*RightOpResult));  } -Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock( +Expected<std::unique_ptr<ExpressionAST>> +Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName, +                       Optional<size_t> LineNumber, +                       FileCheckPatternContext *Context, const SourceMgr &SM) { +  Expr = Expr.ltrim(SpaceChars); +  assert(Expr.startswith("(")); + +  auto OptFunc = StringSwitch<Optional<binop_eval_t>>(FuncName) +                     .Case("add", operator+) +                     .Case("div", operator/) +                     .Case("max", max) +                     .Case("min", min) +                     .Case("mul", operator*) +                     .Case("sub", operator-) +                     .Default(None); + +  if (!OptFunc) +    return ErrorDiagnostic::get( +        SM, FuncName, Twine("call to undefined function '") + FuncName + "'"); + +  Expr.consume_front("("); +  Expr = Expr.ltrim(SpaceChars); + +  // Parse call arguments, which are comma separated. +  SmallVector<std::unique_ptr<ExpressionAST>, 4> Args; +  while (!Expr.empty() && !Expr.startswith(")")) { +    if (Expr.startswith(",")) +      return ErrorDiagnostic::get(SM, Expr, "missing argument"); + +    // Parse the argument, which is an arbitary expression. +    StringRef OuterBinOpExpr = Expr; +    Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand( +        Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber, +        Context, SM); +    while (Arg && !Expr.empty()) { +      Expr = Expr.ltrim(SpaceChars); +      // Have we reached an argument terminator? +      if (Expr.startswith(",") || Expr.startswith(")")) +        break; + +      // Arg = Arg <op> <expr> +      Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber, +                       Context, SM); +    } + +    // Prefer an expression error over a generic invalid argument message. +    if (!Arg) +      return Arg.takeError(); +    Args.push_back(std::move(*Arg)); + +    // Have we parsed all available arguments? +    Expr = Expr.ltrim(SpaceChars); +    if (!Expr.consume_front(",")) +      break; + +    Expr = Expr.ltrim(SpaceChars); +    if (Expr.startswith(")")) +      return ErrorDiagnostic::get(SM, Expr, "missing argument"); +  } + +  if (!Expr.consume_front(")")) +    return ErrorDiagnostic::get(SM, Expr, +                                "missing ')' at end of call expression"); + +  const unsigned NumArgs = Args.size(); +  if (NumArgs == 2) +    return std::make_unique<BinaryOperation>(Expr, *OptFunc, std::move(Args[0]), +                                             std::move(Args[1])); + +  // TODO: Support more than binop_eval_t. +  return ErrorDiagnostic::get(SM, FuncName, +                              Twine("function '") + FuncName + +                                  Twine("' takes 2 arguments but ") + +                                  Twine(NumArgs) + " given"); +} + +Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(      StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,      bool IsLegacyLineExpr, Optional<size_t> LineNumber,      FileCheckPatternContext *Context, const SourceMgr &SM) {    std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;    StringRef DefExpr = StringRef();    DefinedNumericVariable = None; +  ExpressionFormat ExplicitFormat = ExpressionFormat(); + +  // Parse format specifier (NOTE: ',' is also an argument seperator). +  size_t FormatSpecEnd = Expr.find(','); +  size_t FunctionStart = Expr.find('('); +  if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) { +    Expr = Expr.ltrim(SpaceChars); +    if (!Expr.consume_front("%")) +      return ErrorDiagnostic::get( +          SM, Expr, "invalid matching format specification in expression"); + +    // Check for unknown matching format specifier and set matching format in +    // class instance representing this expression. +    SMLoc fmtloc = SMLoc::getFromPointer(Expr.data()); +    switch (popFront(Expr)) { +    case 'u': +      ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned); +      break; +    case 'd': +      ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed); +      break; +    case 'x': +      ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower); +      break; +    case 'X': +      ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper); +      break; +    default: +      return ErrorDiagnostic::get(SM, fmtloc, +                                  "invalid format specifier in expression"); +    } + +    Expr = Expr.ltrim(SpaceChars); +    if (!Expr.consume_front(",")) +      return ErrorDiagnostic::get( +          SM, Expr, "invalid matching format specification in expression"); +  } +    // Save variable definition expression if any.    size_t DefEnd = Expr.find(':');    if (DefEnd != StringRef::npos) { @@ -269,18 +763,30 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(      Expr = Expr.substr(DefEnd + 1);    } +  // Parse matching constraint. +  Expr = Expr.ltrim(SpaceChars); +  bool HasParsedValidConstraint = false; +  if (Expr.consume_front("==")) +    HasParsedValidConstraint = true; +    // Parse the expression itself.    Expr = Expr.ltrim(SpaceChars); -  if (!Expr.empty()) { +  if (Expr.empty()) { +    if (HasParsedValidConstraint) +      return ErrorDiagnostic::get( +          SM, Expr, "empty numeric expression should not have a constraint"); +  } else { +    Expr = Expr.rtrim(SpaceChars); +    StringRef OuterBinOpExpr = Expr;      // The first operand in a legacy @LINE expression is always the @LINE      // pseudo variable.      AllowedOperand AO =          IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; -    Expected<std::unique_ptr<ExpressionAST>> ParseResult = -        parseNumericOperand(Expr, AO, LineNumber, Context, SM); +    Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand( +        Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM);      while (ParseResult && !Expr.empty()) { -      ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, -                               LineNumber, Context, SM); +      ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult), +                               IsLegacyLineExpr, LineNumber, Context, SM);        // Legacy @LINE expressions only allow 2 operands.        if (ParseResult && IsLegacyLineExpr && !Expr.empty())          return ErrorDiagnostic::get( @@ -288,22 +794,42 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(              "unexpected characters at end of expression '" + Expr + "'");      }      if (!ParseResult) -      return ParseResult; +      return ParseResult.takeError();      ExpressionASTPointer = std::move(*ParseResult);    } +  // Select format of the expression, i.e. (i) its explicit format, if any, +  // otherwise (ii) its implicit format, if any, otherwise (iii) the default +  // format (unsigned). Error out in case of conflicting implicit format +  // without explicit format. +  ExpressionFormat Format; +  if (ExplicitFormat) +    Format = ExplicitFormat; +  else if (ExpressionASTPointer) { +    Expected<ExpressionFormat> ImplicitFormat = +        ExpressionASTPointer->getImplicitFormat(SM); +    if (!ImplicitFormat) +      return ImplicitFormat.takeError(); +    Format = *ImplicitFormat; +  } +  if (!Format) +    Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned); + +  std::unique_ptr<Expression> ExpressionPointer = +      std::make_unique<Expression>(std::move(ExpressionASTPointer), Format); +    // Parse the numeric variable definition.    if (DefEnd != StringRef::npos) {      DefExpr = DefExpr.ltrim(SpaceChars); -    Expected<NumericVariable *> ParseResult = -        parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM); +    Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition( +        DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);      if (!ParseResult)        return ParseResult.takeError();      DefinedNumericVariable = *ParseResult;    } -  return std::move(ExpressionASTPointer); +  return std::move(ExpressionPointer);  }  bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -476,10 +1002,10 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,        }        // Parse numeric substitution block. -      std::unique_ptr<ExpressionAST> ExpressionASTPointer; +      std::unique_ptr<Expression> ExpressionPointer;        Optional<NumericVariable *> DefinedNumericVariable;        if (IsNumBlock) { -        Expected<std::unique_ptr<ExpressionAST>> ParseResult = +        Expected<std::unique_ptr<Expression>> ParseResult =              parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,                                            IsLegacyLineExpr, LineNumber, Context,                                            SM); @@ -487,16 +1013,18 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,            logAllUnhandledErrors(ParseResult.takeError(), errs());            return true;          } -        ExpressionASTPointer = std::move(*ParseResult); -        SubstNeeded = ExpressionASTPointer != nullptr; +        ExpressionPointer = std::move(*ParseResult); +        SubstNeeded = ExpressionPointer->getAST() != nullptr;          if (DefinedNumericVariable) {            IsDefinition = true;            DefName = (*DefinedNumericVariable)->getName();          }          if (SubstNeeded)            SubstStr = MatchStr; -        else -          MatchRegexp = "[0-9]+"; +        else { +          ExpressionFormat Format = ExpressionPointer->getFormat(); +          MatchRegexp = cantFail(Format.getWildcardRegex()); +        }        }        // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]]. @@ -554,8 +1082,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,            Substitution *Substitution =                IsNumBlock                    ? Context->makeNumericSubstitution( -                        SubstStr, std::move(ExpressionASTPointer), -                        SubstInsertIdx) +                        SubstStr, std::move(ExpressionPointer), SubstInsertIdx)                    : Context->makeStringSubstitution(SubstStr, SubstInsertIdx);            Substitutions.push_back(Substitution);          } @@ -626,7 +1153,7 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,    if (!Substitutions.empty()) {      TmpStr = RegExStr;      if (LineNumber) -      Context->LineVariable->setValue(*LineNumber); +      Context->LineVariable->setValue(ExpressionValue(*LineNumber));      size_t InsertOffset = 0;      // Substitute all string variables and expressions whose values are only @@ -635,8 +1162,18 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,      for (const auto &Substitution : Substitutions) {        // Substitute and check for failure (e.g. use of undefined variable).        Expected<std::string> Value = Substitution->getResult(); -      if (!Value) -        return Value.takeError(); +      if (!Value) { +        // Convert to an ErrorDiagnostic to get location information. This is +        // done here rather than PrintNoMatch since now we know which +        // substitution block caused the overflow. +        Error Err = +            handleErrors(Value.takeError(), [&](const OverflowError &E) { +              return ErrorDiagnostic::get(SM, Substitution->getFromString(), +                                          "unable to substitute variable or " +                                          "numeric expression: overflow error"); +            }); +        return std::move(Err); +      }        // Plop it into the regex at the adjusted offset.        TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset, @@ -676,11 +1213,12 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen,          NumericVariableMatch.DefinedNumericVariable;      StringRef MatchedValue = MatchInfo[CaptureParenGroup]; -    uint64_t Val; -    if (MatchedValue.getAsInteger(10, Val)) -      return ErrorDiagnostic::get(SM, MatchedValue, -                                  "Unable to represent numeric value"); -    DefinedNumericVariable->setValue(Val); +    ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat(); +    Expected<ExpressionValue> Value = +        Format.valueFromStringRepr(MatchedValue, SM); +    if (!Value) +      return Value.takeError(); +    DefinedNumericVariable->setValue(*Value);    }    // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after @@ -721,17 +1259,20 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,        // variables it uses.        if (!MatchedValue) {          bool UndefSeen = false; -        handleAllErrors(MatchedValue.takeError(), [](const NotFoundError &E) {}, -                        // Handled in PrintNoMatch(). -                        [](const ErrorDiagnostic &E) {}, -                        [&](const UndefVarError &E) { -                          if (!UndefSeen) { -                            OS << "uses undefined variable(s):"; -                            UndefSeen = true; -                          } -                          OS << " "; -                          E.log(OS); -                        }); +        handleAllErrors( +            MatchedValue.takeError(), [](const NotFoundError &E) {}, +            // Handled in PrintNoMatch(). +            [](const ErrorDiagnostic &E) {}, +            // Handled in match(). +            [](const OverflowError &E) {}, +            [&](const UndefVarError &E) { +              if (!UndefSeen) { +                OS << "uses undefined variable(s):"; +                UndefSeen = true; +              } +              OS << " "; +              E.log(OS); +            });        } else {          // Substitution succeeded. Print substituted value.          OS << "with \""; @@ -837,10 +1378,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName,  }  Substitution *FileCheckPatternContext::makeNumericSubstitution( -    StringRef ExpressionStr, -    std::unique_ptr<ExpressionAST> ExpressionASTPointer, size_t InsertIdx) { +    StringRef ExpressionStr, std::unique_ptr<Expression> Expression, +    size_t InsertIdx) {    Substitutions.push_back(std::make_unique<NumericSubstitution>( -      this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx)); +      this, ExpressionStr, std::move(Expression), InsertIdx));    return Substitutions.back().get();  } @@ -915,20 +1456,17 @@ FileCheckDiag::FileCheckDiag(const SourceMgr &SM,                               const Check::FileCheckType &CheckTy,                               SMLoc CheckLoc, MatchType MatchTy,                               SMRange InputRange) -    : CheckTy(CheckTy), MatchTy(MatchTy) { +    : CheckTy(CheckTy), CheckLoc(CheckLoc), MatchTy(MatchTy) {    auto Start = SM.getLineAndColumn(InputRange.Start);    auto End = SM.getLineAndColumn(InputRange.End);    InputStartLine = Start.first;    InputStartCol = Start.second;    InputEndLine = End.first;    InputEndCol = End.second; -  Start = SM.getLineAndColumn(CheckLoc); -  CheckLine = Start.first; -  CheckCol = Start.second;  }  static bool IsPartOfWord(char c) { -  return (isalnum(c) || c == '-' || c == '_'); +  return (isAlnum(c) || c == '-' || c == '_');  }  Check::FileCheckType &Check::FileCheckType::setCount(int C) { @@ -946,7 +1484,7 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const {    case Check::CheckPlain:      if (Count > 1)        return Prefix.str() + "-COUNT"; -    return Prefix; +    return std::string(Prefix);    case Check::CheckNext:      return Prefix.str() + "-NEXT";    case Check::CheckSame: @@ -959,6 +1497,8 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const {      return Prefix.str() + "-LABEL";    case Check::CheckEmpty:      return Prefix.str() + "-EMPTY"; +  case Check::CheckComment: +    return std::string(Prefix);    case Check::CheckEOF:      return "implicit EOF";    case Check::CheckBadNot: @@ -970,13 +1510,24 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const {  }  static std::pair<Check::FileCheckType, StringRef> -FindCheckType(StringRef Buffer, StringRef Prefix) { +FindCheckType(const FileCheckRequest &Req, StringRef Buffer, StringRef Prefix) {    if (Buffer.size() <= Prefix.size())      return {Check::CheckNone, StringRef()};    char NextChar = Buffer[Prefix.size()];    StringRef Rest = Buffer.drop_front(Prefix.size() + 1); + +  // Check for comment. +  if (Req.CommentPrefixes.end() != std::find(Req.CommentPrefixes.begin(), +                                             Req.CommentPrefixes.end(), +                                             Prefix)) { +    if (NextChar == ':') +      return {Check::CheckComment, Rest}; +    // Ignore a comment prefix if it has a suffix like "-NOT". +    return {Check::CheckNone, StringRef()}; +  } +    // Verify that the : is present after the prefix.    if (NextChar == ':')      return {Check::CheckPlain, Rest}; @@ -1055,8 +1606,9 @@ static size_t SkipWord(StringRef Str, size_t Loc) {  /// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy  /// is unspecified.  static std::pair<StringRef, StringRef> -FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer, -                        unsigned &LineNumber, Check::FileCheckType &CheckTy) { +FindFirstMatchingPrefix(const FileCheckRequest &Req, Regex &PrefixRE, +                        StringRef &Buffer, unsigned &LineNumber, +                        Check::FileCheckType &CheckTy) {    SmallVector<StringRef, 2> Matches;    while (!Buffer.empty()) { @@ -1084,7 +1636,7 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,      if (Skipped.empty() || !IsPartOfWord(Skipped.back())) {        // Now extract the type.        StringRef AfterSuffix; -      std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix); +      std::tie(CheckTy, AfterSuffix) = FindCheckType(Req, Buffer, Prefix);        // If we've found a valid check type for this prefix, we're done.        if (CheckTy != Check::CheckNone) @@ -1104,7 +1656,8 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,  void FileCheckPatternContext::createLineVariable() {    assert(!LineVariable && "@LINE pseudo numeric variable already created");    StringRef LineName = "@LINE"; -  LineVariable = makeNumericVariable(LineName); +  LineVariable = makeNumericVariable( +      LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));    GlobalNumericVariableTable[LineName] = LineVariable;  } @@ -1114,8 +1667,12 @@ FileCheck::FileCheck(FileCheckRequest Req)  FileCheck::~FileCheck() = default; -bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, -                              Regex &PrefixRE) { +bool FileCheck::readCheckFile( +    SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, +    std::pair<unsigned, unsigned> *ImpPatBufferIDRange) { +  if (ImpPatBufferIDRange) +    ImpPatBufferIDRange->first = ImpPatBufferIDRange->second = 0; +    Error DefineError =        PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM);    if (DefineError) { @@ -1126,17 +1683,27 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,    PatternContext->createLineVariable();    std::vector<Pattern> ImplicitNegativeChecks; -  for (const auto &PatternString : Req.ImplicitCheckNot) { +  for (StringRef PatternString : Req.ImplicitCheckNot) {      // Create a buffer with fake command line content in order to display the      // command line option responsible for the specific implicit CHECK-NOT.      std::string Prefix = "-implicit-check-not='";      std::string Suffix = "'";      std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy( -        Prefix + PatternString + Suffix, "command line"); +        (Prefix + PatternString + Suffix).str(), "command line");      StringRef PatternInBuffer =          CmdLine->getBuffer().substr(Prefix.size(), PatternString.size()); -    SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); +    unsigned BufferID = SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); +    if (ImpPatBufferIDRange) { +      if (ImpPatBufferIDRange->first == ImpPatBufferIDRange->second) { +        ImpPatBufferIDRange->first = BufferID; +        ImpPatBufferIDRange->second = BufferID + 1; +      } else { +        assert(BufferID == ImpPatBufferIDRange->second && +               "expected consecutive source buffer IDs"); +        ++ImpPatBufferIDRange->second; +      } +    }      ImplicitNegativeChecks.push_back(          Pattern(Check::CheckNot, PatternContext.get())); @@ -1150,6 +1717,7 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,    // found.    unsigned LineNumber = 1; +  bool FoundUsedCheckPrefix = false;    while (1) {      Check::FileCheckType CheckTy; @@ -1157,9 +1725,12 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,      StringRef UsedPrefix;      StringRef AfterSuffix;      std::tie(UsedPrefix, AfterSuffix) = -        FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy); +        FindFirstMatchingPrefix(Req, PrefixRE, Buffer, LineNumber, CheckTy);      if (UsedPrefix.empty())        break; +    if (CheckTy != Check::CheckComment) +      FoundUsedCheckPrefix = true; +      assert(UsedPrefix.data() == Buffer.data() &&             "Failed to move Buffer's start forward, or pointed prefix outside "             "of the buffer!"); @@ -1201,9 +1772,17 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,      // Remember the location of the start of the pattern, for diagnostics.      SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); +    // Extract the pattern from the buffer. +    StringRef PatternBuffer = Buffer.substr(0, EOL); +    Buffer = Buffer.substr(EOL); + +    // If this is a comment, we're done. +    if (CheckTy == Check::CheckComment) +      continue; +      // Parse the pattern.      Pattern P(CheckTy, PatternContext.get(), LineNumber); -    if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req)) +    if (P.parsePattern(PatternBuffer, UsedPrefix, SM, Req))        return true;      // Verify that CHECK-LABEL lines do not define or use variables @@ -1215,8 +1794,6 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,        return true;      } -    Buffer = Buffer.substr(EOL); -      // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them.      if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame ||           CheckTy == Check::CheckEmpty) && @@ -1243,31 +1820,30 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer,      DagNotMatches = ImplicitNegativeChecks;    } -  // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first -  // prefix as a filler for the error message. -  if (!DagNotMatches.empty()) { -    CheckStrings->emplace_back( -        Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1), -        *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); -    std::swap(DagNotMatches, CheckStrings->back().DagNotStrings); -  } - -  if (CheckStrings->empty()) { +  // When there are no used prefixes we report an error except in the case that +  // no prefix is specified explicitly but -implicit-check-not is specified. +  if (!FoundUsedCheckPrefix && +      (ImplicitNegativeChecks.empty() || !Req.IsDefaultCheckPrefix)) {      errs() << "error: no check strings found with prefix"             << (Req.CheckPrefixes.size() > 1 ? "es " : " "); -    auto I = Req.CheckPrefixes.begin(); -    auto E = Req.CheckPrefixes.end(); -    if (I != E) { -      errs() << "\'" << *I << ":'"; -      ++I; +    for (size_t I = 0, E = Req.CheckPrefixes.size(); I != E; ++I) { +      if (I != 0) +        errs() << ", "; +      errs() << "\'" << Req.CheckPrefixes[I] << ":'";      } -    for (; I != E; ++I) -      errs() << ", \'" << *I << ":'"; -      errs() << '\n';      return true;    } +  // Add an EOF pattern for any trailing --implicit-check-not/CHECK-DAG/-NOTs, +  // and use the first prefix as a filler for the error message. +  if (!DagNotMatches.empty()) { +    CheckStrings->emplace_back( +        Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1), +        *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); +    std::swap(DagNotMatches, CheckStrings->back().DagNotStrings); +  } +    return false;  } @@ -1706,43 +2282,74 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,    return StartPos;  } -// A check prefix must contain only alphanumeric, hyphens and underscores. -static bool ValidateCheckPrefix(StringRef CheckPrefix) { -  static const Regex Validator("^[a-zA-Z0-9_-]*$"); -  return Validator.match(CheckPrefix); -} - -bool FileCheck::ValidateCheckPrefixes() { -  StringSet<> PrefixSet; - -  for (StringRef Prefix : Req.CheckPrefixes) { -    // Reject empty prefixes. -    if (Prefix == "") +static bool ValidatePrefixes(StringRef Kind, StringSet<> &UniquePrefixes, +                             ArrayRef<StringRef> SuppliedPrefixes) { +  for (StringRef Prefix : SuppliedPrefixes) { +    if (Prefix.empty()) { +      errs() << "error: supplied " << Kind << " prefix must not be the empty " +             << "string\n";        return false; - -    if (!PrefixSet.insert(Prefix).second) +    } +    static const Regex Validator("^[a-zA-Z0-9_-]*$"); +    if (!Validator.match(Prefix)) { +      errs() << "error: supplied " << Kind << " prefix must start with a " +             << "letter and contain only alphanumeric characters, hyphens, and " +             << "underscores: '" << Prefix << "'\n";        return false; - -    if (!ValidateCheckPrefix(Prefix)) +    } +    if (!UniquePrefixes.insert(Prefix).second) { +      errs() << "error: supplied " << Kind << " prefix must be unique among " +             << "check and comment prefixes: '" << Prefix << "'\n";        return false; +    }    } +  return true; +} + +static const char *DefaultCheckPrefixes[] = {"CHECK"}; +static const char *DefaultCommentPrefixes[] = {"COM", "RUN"}; +bool FileCheck::ValidateCheckPrefixes() { +  StringSet<> UniquePrefixes; +  // Add default prefixes to catch user-supplied duplicates of them below. +  if (Req.CheckPrefixes.empty()) { +    for (const char *Prefix : DefaultCheckPrefixes) +      UniquePrefixes.insert(Prefix); +  } +  if (Req.CommentPrefixes.empty()) { +    for (const char *Prefix : DefaultCommentPrefixes) +      UniquePrefixes.insert(Prefix); +  } +  // Do not validate the default prefixes, or diagnostics about duplicates might +  // incorrectly indicate that they were supplied by the user. +  if (!ValidatePrefixes("check", UniquePrefixes, Req.CheckPrefixes)) +    return false; +  if (!ValidatePrefixes("comment", UniquePrefixes, Req.CommentPrefixes)) +    return false;    return true;  }  Regex FileCheck::buildCheckPrefixRegex() { -  // I don't think there's a way to specify an initial value for cl::list, -  // so if nothing was specified, add the default -  if (Req.CheckPrefixes.empty()) -    Req.CheckPrefixes.push_back("CHECK"); +  if (Req.CheckPrefixes.empty()) { +    for (const char *Prefix : DefaultCheckPrefixes) +      Req.CheckPrefixes.push_back(Prefix); +    Req.IsDefaultCheckPrefix = true; +  } +  if (Req.CommentPrefixes.empty()) { +    for (const char *Prefix : DefaultCommentPrefixes) +      Req.CommentPrefixes.push_back(Prefix); +  } -  // We already validated the contents of CheckPrefixes so just concatenate -  // them as alternatives. +  // We already validated the contents of CheckPrefixes and CommentPrefixes so +  // just concatenate them as alternatives.    SmallString<32> PrefixRegexStr; -  for (StringRef Prefix : Req.CheckPrefixes) { -    if (Prefix != Req.CheckPrefixes.front()) +  for (size_t I = 0, E = Req.CheckPrefixes.size(); I != E; ++I) { +    if (I != 0)        PrefixRegexStr.push_back('|'); - +    PrefixRegexStr.append(Req.CheckPrefixes[I]); +  } +  for (StringRef Prefix : Req.CommentPrefixes) { +    PrefixRegexStr.push_back('|');      PrefixRegexStr.append(Prefix);    } @@ -1750,7 +2357,7 @@ Regex FileCheck::buildCheckPrefixRegex() {  }  Error FileCheckPatternContext::defineCmdlineVariables( -    std::vector<std::string> &CmdlineDefines, SourceMgr &SM) { +    ArrayRef<StringRef> CmdlineDefines, SourceMgr &SM) {    assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&           "Overriding defined variable with command-line variable definitions"); @@ -1777,7 +2384,7 @@ Error FileCheckPatternContext::defineCmdlineVariables(        // format as in the input file to be able to reuse        // parseNumericSubstitutionBlock.        CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str(); -      std::string SubstitutionStr = CmdlineDef; +      std::string SubstitutionStr = std::string(CmdlineDef);        SubstitutionStr[EqIdx] = ':';        CmdlineDefsIndices.push_back(            std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size())); @@ -1815,20 +2422,19 @@ Error FileCheckPatternContext::defineCmdlineVariables(        // to create the necessary class instance.        StringRef CmdlineDefExpr = CmdlineDef.substr(1);        Optional<NumericVariable *> DefinedNumericVariable; -      Expected<std::unique_ptr<ExpressionAST>> ExpressionASTResult = +      Expected<std::unique_ptr<Expression>> ExpressionResult =            Pattern::parseNumericSubstitutionBlock(                CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM); -      if (!ExpressionASTResult) { -        Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError()); +      if (!ExpressionResult) { +        Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());          continue;        } -      std::unique_ptr<ExpressionAST> ExpressionASTPointer = -          std::move(*ExpressionASTResult); +      std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);        // Now evaluate the expression whose value this variable should be set        // to, since the expression of a command-line variable definition should        // only use variables defined earlier on the command-line. If not, this        // is an error and we report it. -      Expected<uint64_t> Value = ExpressionASTPointer->eval(); +      Expected<ExpressionValue> Value = Expression->getAST()->eval();        if (!Value) {          Errs = joinErrors(std::move(Errs), Value.takeError());          continue;  | 
