From: peter klausler Date: Mon, 5 Feb 2018 20:48:09 +0000 (-0800) Subject: [flang] Variadic macros, __VA_ARG__ and __VA_OPT__, and use X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=be830d6b356dc8fdba60cb6b081ec541ce82e2e2;p=platform%2Fupstream%2Fllvm.git [flang] Variadic macros, __VA_ARG__ and __VA_OPT__, and use "default: CRASH_NO_CASE;" in place of prior macro. Original-commit: flang-compiler/f18@21088961554347d28e88e14f9c82eeb0987867af --- diff --git a/flang/idioms.cc b/flang/idioms.cc index 6510e19..cfdda03 100644 --- a/flang/idioms.cc +++ b/flang/idioms.cc @@ -5,7 +5,7 @@ namespace Fortran { -void die(const char *msg, ...) { +[[noreturn]] void die(const char *msg, ...) { va_list ap; va_start(ap, msg); std::fputs("\nfatal internal error: ", stderr); diff --git a/flang/idioms.h b/flang/idioms.h index b455c1f..887d3b9 100644 --- a/flang/idioms.h +++ b/flang/idioms.h @@ -52,7 +52,7 @@ template visitors(LAMBDAS... x) -> visitors; // Calls std::fprintf(stderr, ...), then abort(). -void die(const char *, ...); +[[noreturn]] void die(const char *, ...); // Treat operator! as if it were a Boolean context, i.e. like if() and ? :, // when its operand is std::optional<>. @@ -62,8 +62,8 @@ template bool operator!(const std::optional &x) { } // namespace Fortran // For switch statements without default: labels. -#define DEFAULT_CRASH \ - default: die("no case at " __FILE__ "(%d)", __LINE__) +#define CRASH_NO_CASE \ + die("no case at " __FILE__ "(%d)", __LINE__) // For cheap assertions that should be applied in production. #define CHECK(x) \ diff --git a/flang/parse-tree.cc b/flang/parse-tree.cc index 52f950e..aa3dfce 100644 --- a/flang/parse-tree.cc +++ b/flang/parse-tree.cc @@ -517,7 +517,7 @@ std::ostream &operator<<(std::ostream &o, case DefinedOperator::IntrinsicOperator::OR: return o << "OR"; case DefinedOperator::IntrinsicOperator::EQV: return o << "EQV"; case DefinedOperator::IntrinsicOperator::NEQV: return o << "NEQV"; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -560,7 +560,7 @@ std::ostream &operator<<(std::ostream &o, Sign x) { switch (x) { case Sign::Positive: return o << "Positive"; case Sign::Negative: return o << "Negative"; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -617,7 +617,7 @@ std::ostream &operator<<(std::ostream &o, switch (x) { case TypeParamDefStmt::KindOrLength::Kind: o << "Kind"; break; case TypeParamDefStmt::KindOrLength::Length: o << "Length"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -645,7 +645,7 @@ std::ostream &operator<<(std::ostream &o, const AccessSpec &x) { switch (x.v) { case AccessSpec::Kind::Public: return o << "(Public)"; case AccessSpec::Kind::Private: return o << "(Private)"; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -656,7 +656,7 @@ std::ostream &operator<<(std::ostream &o, const IntentSpec &x) { case IntentSpec::Intent::In: return o << "(Intent In)"; case IntentSpec::Intent::Out: return o << "(Intent Out)"; case IntentSpec::Intent::InOut: return o << "(Intent InOut)"; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -678,7 +678,7 @@ std::ostream &operator<<(std::ostream &o, switch (x) { case ImplicitStmt::ImplicitNoneNameSpec::External: return o << "External"; case ImplicitStmt::ImplicitNoneNameSpec::Type: return o << "Type"; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -700,7 +700,7 @@ std::ostream &operator<<(std::ostream &o, const ImportStmt &x) { return o << "None)"; case ImportStmt::Kind::All: return o << "All)"; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -895,7 +895,7 @@ std::ostream &operator<<(std::ostream &o, const ConnectSpec::CharExpr::Kind x) { case ConnectSpec::CharExpr::Kind::Round: o << "Round"; break; case ConnectSpec::CharExpr::Kind::Sign: o << "Sign"; break; case ConnectSpec::CharExpr::Kind::Dispose: o << "Dispose"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -910,7 +910,7 @@ std::ostream &operator<<(std::ostream &o, IoControlSpec::CharExpr::Kind x) { case IoControlSpec::CharExpr::Kind::Pad: o << "Pad"; break; case IoControlSpec::CharExpr::Kind::Round: o << "Round"; break; case IoControlSpec::CharExpr::Kind::Sign: o << "Sign"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -939,7 +939,7 @@ std::ostream &operator<<(std::ostream &o, const InquireSpec::CharVar::Kind x) { case InquireSpec::CharVar::Kind::Stream: o << "Stream"; break; case InquireSpec::CharVar::Kind::Status: o << "Status"; break; case InquireSpec::CharVar::Kind::Write: o << "Write"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -952,7 +952,7 @@ std::ostream &operator<<(std::ostream &o, const InquireSpec::IntVar::Kind x) { case InquireSpec::IntVar::Kind::Pos: o << "Pos"; break; case InquireSpec::IntVar::Kind::Recl: o << "Recl"; break; case InquireSpec::IntVar::Kind::Size: o << "Size"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -963,7 +963,7 @@ std::ostream &operator<<(std::ostream &o, const InquireSpec::LogVar::Kind x) { case InquireSpec::LogVar::Kind::Named: o << "Named"; break; case InquireSpec::LogVar::Kind::Opened: o << "Opened"; break; case InquireSpec::LogVar::Kind::Pending: o << "Pending"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -985,7 +985,7 @@ std::ostream &operator<<(std::ostream &o, const IntrinsicTypeDataEditDesc &x) { case IntrinsicTypeDataEditDesc::Kind::L: o << "L "; break; case IntrinsicTypeDataEditDesc::Kind::A: o << "A "; break; case IntrinsicTypeDataEditDesc::Kind::D: o << "D "; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o << x.width << ' ' << x.digits << ' ' << x.exponentWidth << ')'; } @@ -995,7 +995,7 @@ std::ostream &operator<<(std::ostream &o, StopStmt::Kind x) { switch (x) { case StopStmt::Kind::Stop: o << "Stop"; break; case StopStmt::Kind::ErrorStop: o << "ErrorStop"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } @@ -1041,7 +1041,7 @@ std::ostream &operator<<(std::ostream &o, const ControlEditDesc &x) { case ControlEditDesc::Kind::RP: o << "RP "; break; case ControlEditDesc::Kind::DC: o << "DC "; break; case ControlEditDesc::Kind::DP: o << "DP "; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o << x.count << ')'; } @@ -1064,7 +1064,7 @@ std::ostream &operator<<(std::ostream &o, const UseStmt &x) { case UseStmt::ModuleNature::Non_Intrinsic: o << "Non_Intrinsic"; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } } else { o << "()"; @@ -1082,7 +1082,7 @@ std::ostream &operator<<(std::ostream &o, const ProcedureStmt::Kind &x) { switch (x) { case ProcedureStmt::Kind::ModuleProcedure: return o << "ModuleProcedure"; case ProcedureStmt::Kind::Procedure: return o << "Procedure"; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } return o; } diff --git a/flang/preprocessor.cc b/flang/preprocessor.cc index 9fe3fff..8c4882e 100644 --- a/flang/preprocessor.cc +++ b/flang/preprocessor.cc @@ -57,14 +57,50 @@ std::string TokenSequence::ToString() const { return {&char_[0], char_.size()}; } +void TokenSequence::clear() { + start_.clear(); + nextStart_ = 0; + char_.clear(); +} + +void TokenSequence::push_back(const char *s, size_t bytes) { + for (size_t j{0}; j < bytes; ++j) { + AddChar(s[j]); + } + EndToken(); +} + +void TokenSequence::push_back(const CharPointerWithLength &t) { + push_back(&t[0], t.size()); +} +void TokenSequence::push_back(const std::string &s) { + push_back(s.data(), s.size()); +} + +void TokenSequence::push_back(const std::stringstream &ss) { + push_back(ss.str()); +} + +void TokenSequence::pop_back() { + nextStart_ = start_.back(); + start_.pop_back(); + char_.resize(nextStart_); +} + +void TokenSequence::shrink_to_fit() { + start_.shrink_to_fit(); + char_.shrink_to_fit(); +} + Definition::Definition(const TokenSequence &repl, size_t firstToken, size_t tokens) : replacement_{Tokenize({}, repl, firstToken, tokens)} {} Definition::Definition(const std::vector &argNames, const TokenSequence &repl, size_t firstToken, - size_t tokens) + size_t tokens, bool isVariadic) : isFunctionLike_{true}, argumentCount_(argNames.size()), + isVariadic_{isVariadic}, replacement_{Tokenize(argNames, repl, firstToken, tokens)} {} Definition::Definition(const std::string &predefined) @@ -95,19 +131,15 @@ TokenSequence Definition::Tokenize(const std::vector &argNames, } TokenSequence result; for (size_t j{0}; j < tokens; ++j) { - size_t bytes{token[firstToken + j].size()}; - if (bytes == 0) { - continue; - } - const char *text{token[firstToken + j].data()}; - if (bytes > 0 && IsIdentifierFirstCharacter(*text)) { - auto it = args.find(token[firstToken + j].ToString()); + CharPointerWithLength tok{token[firstToken + j]}; + if (IsIdentifierFirstCharacter(tok)) { + auto it = args.find(tok.ToString()); if (it != args.end()) { result.push_back(it->second); continue; } } - result.push_back(text, bytes); + result.push_back(tok); } return result; } @@ -115,13 +147,24 @@ TokenSequence Definition::Tokenize(const std::vector &argNames, TokenSequence Definition::Apply(const std::vector &args) { TokenSequence result; bool pasting{false}; + bool skipping{false}; + int parenthesesNesting{0}; size_t tokens{replacement_.size()}; for (size_t j{0}; j < tokens; ++j) { const CharPointerWithLength &token{replacement_[j]}; size_t bytes{token.size()}; - const char *text{token.data()}; - if (bytes == 2 && *text == '~') { - size_t index = text[1] - 'A'; + if (skipping) { + if (bytes == 1) { + if (token[0] == '(') { + ++parenthesesNesting; + } else if (token[0] == ')') { + skipping = --parenthesesNesting > 0; + } + } + continue; + } + if (bytes == 2 && token[0] == '~') { + size_t index = token[1] - 'A'; if (index >= args.size()) { continue; } @@ -138,8 +181,8 @@ TokenSequence Definition::Apply(const std::vector &args) { } std::string strung{'"'}; for (size_t k{0}; k < argTokens; ++k) { + const CharPointerWithLength &arg{args[index][k]}; size_t argBytes{args[index][k].size()}; - const char *arg{args[index][k].data()}; for (size_t n{0}; n < argBytes; ++n) { char ch{arg[n]}; if (ch == '"' || ch == '\\') { @@ -159,7 +202,7 @@ TokenSequence Definition::Apply(const std::vector &args) { } } } - } else if (bytes == 2 && text[0] == '#' && text[1] == '#') { + } else if (bytes == 2 && token[0] == '#' && token[1] == '#') { // Token pasting operator in body (not expanded argument); discard any // immediately preceding white space, then reopen the last token. while (!result.empty() && result[result.size() - 1].IsBlank()) { @@ -171,7 +214,33 @@ TokenSequence Definition::Apply(const std::vector &args) { } } else if (pasting && token.IsBlank()) { // Delete whitespace immediately following ## in the body. + } else if (bytes == 11 && isVariadic_ && + token.ToString() == "__VA_ARGS__") { + for (size_t k{argumentCount_}; k < args.size(); ++k) { + if (k > argumentCount_) { + result.push_back(","s); + } + for (size_t n{0}; n < args[k].size(); ++n) { + result.push_back(args[k][n]); + } + } + } else if (bytes == 10 && isVariadic_ && + token.ToString() == "__VA_OPT__" && + j + 2 < tokens && + replacement_[j + 1].ToString() == "(" && + parenthesesNesting == 0) { + parenthesesNesting = 1; + skipping = args.size() == argumentCount_; + ++j; } else { + if (bytes == 1 && parenthesesNesting > 0 && token[0] == '(') { + ++parenthesesNesting; + } else if (bytes == 1 && parenthesesNesting > 0 && token[0] == ')') { + if (--parenthesesNesting == 0) { + skipping = false; + continue; + } + } result.push_back(token); } } @@ -285,7 +354,8 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input, } } if (k >= tokens || - argStart.size() != def.argumentCount()) { + argStart.size() < def.argumentCount() || + (argStart.size() > def.argumentCount() && !def.isVariadic())) { result->push_back(token); continue; } @@ -296,7 +366,7 @@ bool Preprocessor::MacroReplacement(const TokenSequence &input, size_t count{(k + 1 == argStart.size() ? j : argStart[k+1] - 1) - at}; TokenSequence actual; for (; count-- > 0; ++at) { - actual.push_back(input[at].data(), input[at].size()); + actual.push_back(input[at]); } args.emplace_back(std::move(actual)); } @@ -393,14 +463,19 @@ bool Preprocessor::Directive(const TokenSequence &dir) { if (++j < tokens && dir[j].size() == 1 && dir[j][0] == '(') { j = SkipBlanks(dir, j + 1, tokens); std::vector argName; + bool isVariadic{false}; if (dir[j].ToString() != ")") { while (true) { std::string an{dir[j].ToString()}; - if (an.empty() || !IsIdentifierFirstCharacter(an[0])) { - Complain("#define: missing or invalid argument name"); - return false; + if (an == "...") { + isVariadic = true; + } else { + if (an.empty() || !IsIdentifierFirstCharacter(an[0])) { + Complain("#define: missing or invalid argument name"); + return false; + } + argName.push_back(an); } - argName.push_back(an); j = SkipBlanks(dir, j + 1, tokens); if (j == tokens) { Complain("#define: malformed argument list"); @@ -415,7 +490,7 @@ bool Preprocessor::Directive(const TokenSequence &dir) { return false; } j = SkipBlanks(dir, j + 1, tokens); - if (j == tokens) { + if (j == tokens || isVariadic) { Complain("#define: malformed argument list"); return false; } @@ -428,7 +503,8 @@ bool Preprocessor::Directive(const TokenSequence &dir) { } j = SkipBlanks(dir, j + 1, tokens); definitions_.emplace( - std::make_pair(nameToken, Definition{argName, dir, j, tokens - j})); + std::make_pair(nameToken, + Definition{argName, dir, j, tokens - j, isVariadic})); } else { definitions_.emplace( std::make_pair(nameToken, Definition{dir, j, tokens - j})); @@ -560,21 +636,22 @@ void Preprocessor::Complain(const std::string &message) { } // Precedence level codes used here to accommodate mixed Fortran and C: -// 13: parentheses and constants, logical !, bitwise ~ -// 12: unary + and - -// 11: ** -// 10: *, /, % (modulus) -// 9: + and - -// 8: << and >> -// 7: bitwise & -// 6: bitwise ^ -// 5: bitwise | -// 4: relations (.EQ., ==, &c.) -// 3: .NOT. -// 2: .AND., && -// 1: .OR., || -// 0: .EQV. and .NEQV. / .XOR. -// TODO: Ternary and comma operators? +// 15: parentheses and constants, logical !, bitwise ~ +// 14: unary + and - +// 13: ** +// 12: *, /, % (modulus) +// 11: + and - +// 0: << and >> +// 9: bitwise & +// 8: bitwise ^ +// 7: bitwise | +// 6: relations (.EQ., ==, &c.) +// 5: .NOT. +// 4: .AND., && +// 3: .OR., || +// 2: .EQV. and .NEQV. / .XOR. +// 1: ? : +// 0: , static std::int64_t ExpressionValue(const TokenSequence &token, int minimumPrecedence, size_t *atToken, std::string *errors) { @@ -583,23 +660,26 @@ static std::int64_t ExpressionValue(const TokenSequence &token, TIMES, DIVIDE, MODULUS, ADD, SUBTRACT, LEFTSHIFT, RIGHTSHIFT, BITAND, BITXOR, BITOR, LT, LE, EQ, NE, GE, GT, - NOT, AND, OR, EQV, NEQV + NOT, AND, OR, EQV, NEQV, + SELECT, COMMA }; static const int precedence[]{ - 13, 13, 13, 13, // (), 0, !, ~ - 12, 12, // unary +, - - 11, 10, 10, 10, 9, 9, 8, 8, // **, *, /, %, +, -, <<, >> - 7, 6, 5, // &, ^, | - 4, 4, 4, 4, 4, 4, // relations - 3, 2, 1, 0, 0 // .NOT., .AND., .OR., .EQV., .NEQV. + 15, 15, 15, 15, // (), 0, !, ~ + 14, 14, // unary +, - + 13, 12, 12, 12, 11, 11, 10, 10, // **, *, /, %, +, -, <<, >> + 9, 8, 7, // &, ^, | + 6, 6, 6, 6, 6, 6, // relations + 5, 4, 3, 2, 2, // .NOT., .AND., .OR., .EQV., .NEQV. + 1, 0 // ?: and , }; static const int operandPrecedence[]{ - 0, -1, 13, 13, - 13, 13, - 11, 10, 10, 10, 9, 9, 9, 9, - 7, 6, 5, - 5, 5, 5, 5, 5, 5, - 4, 2, 1, 1, 1 + 0, -1, 15, 15, + 15, 15, + 13, 12, 12, 12, 11, 11, 11, 11, + 9, 8, 7, + 7, 7, 7, 7, 7, 7, + 6, 4, 3, 3, 3, + 1, 0 }; static std::map opNameMap; @@ -629,6 +709,8 @@ static std::int64_t ExpressionValue(const TokenSequence &token, opNameMap[".or."] = opNameMap[".o."] = opNameMap["||"] = OR; opNameMap[".eqv."] = EQV; opNameMap[".neqv."] = opNameMap[".xor."] = opNameMap[".x."] = NEQV; + opNameMap["?"] = SELECT; + opNameMap[","] = COMMA; } size_t tokens{token.size()}; @@ -700,7 +782,7 @@ static std::int64_t ExpressionValue(const TokenSequence &token, case NOT: left = -!left; break; - DEFAULT_CRASH; + default: CRASH_NO_CASE; } } if (!errors->empty() || *atToken >= tokens) { @@ -813,7 +895,19 @@ static std::int64_t ExpressionValue(const TokenSequence &token, return -(!left == !right); case NEQV: return -(!left != !right); - DEFAULT_CRASH; + case SELECT: + if (*atToken >= tokens || token[*atToken].ToString() != ":") { + *errors = "':' required in selection expression"; + return left; + } else { + ++*atToken; + std::int64_t third{ExpressionValue(token, operandPrecedence[op], + atToken, errors)}; + return left != 0 ? right : third; + } + case COMMA: + return right; + default: CRASH_NO_CASE; } return 0; // silence compiler warning } diff --git a/flang/preprocessor.h b/flang/preprocessor.h index 29f8d1b..6c473b0 100644 --- a/flang/preprocessor.h +++ b/flang/preprocessor.h @@ -41,7 +41,6 @@ class CharPointerWithLength { bool empty() const { return bytes_ == 0; } size_t size() const { return bytes_; } - const char *data() const { return data_; } const char &operator[](size_t j) const { return data_[j]; } bool IsBlank() const; @@ -56,10 +55,9 @@ class CharPointerWithLength { // Specializations to enable std::unordered_map template<> struct std::hash { size_t operator()(const Fortran::CharPointerWithLength &x) const { - size_t hash{0}; - const char *p{x.data()}, *limit{p + x.size()}; - for (; p < limit; ++p) { - hash = (hash * 31) ^ *p; + size_t hash{0}, bytes{x.size()}; + for (size_t j{0}; j < bytes; ++j) { + hash = (hash * 31) ^ x[j]; } return hash; } @@ -69,8 +67,8 @@ template<> struct std::equal_to { bool operator()(const Fortran::CharPointerWithLength &x, const Fortran::CharPointerWithLength &y) const { return x.size() == y.size() && - std::memcmp(static_cast(x.data()), - static_cast(y.data()), + std::memcmp(static_cast(&x[0]), + static_cast(&y[0]), x.size()) == 0; } }; @@ -128,36 +126,13 @@ class TokenSequence { bool empty() const { return start_.empty(); } size_t size() const { return start_.size(); } const char *data() const { return &char_[0]; } - - void clear() { - start_.clear(); - nextStart_ = 0; - char_.clear(); - } - - void push_back(const char *s, size_t bytes) { - for (size_t j{0}; j < bytes; ++j) { - AddChar(s[j]); - } - EndToken(); - } - - void push_back(const CharPointerWithLength &t) { - push_back(t.data(), t.size()); - } - - void push_back(const std::stringstream &ss) { push_back(ss.str()); } - - void pop_back() { - nextStart_ = start_.back(); - start_.pop_back(); - char_.resize(nextStart_); - } - - void shrink_to_fit() { - start_.shrink_to_fit(); - char_.shrink_to_fit(); - } + void clear(); + void push_back(const char *, size_t); + void push_back(const CharPointerWithLength &); + void push_back(const std::string &); + void push_back(const std::stringstream &); + void pop_back(); + void shrink_to_fit(); private: std::vector start_; @@ -170,7 +145,7 @@ class Definition { public: Definition(const TokenSequence &, size_t firstToken, size_t tokens); Definition(const std::vector &argNames, const TokenSequence &, - size_t firstToken, size_t tokens); + size_t firstToken, size_t tokens, bool isVariadic = false); explicit Definition(const std::string &predefined); bool isFunctionLike() const { return isFunctionLike_; } diff --git a/flang/prescan.cc b/flang/prescan.cc index 9d1a0d9..f554889 100644 --- a/flang/prescan.cc +++ b/flang/prescan.cc @@ -216,10 +216,13 @@ bool Prescanner::NextToken(TokenSequence *tokens) { } preventHollerith_ = false; } else if (*at_ == '.') { - if (isdigit(EmitCharAndAdvance(tokens, '.'))) { + char nch{EmitCharAndAdvance(tokens, '.')}; + if (isdigit(nch)) { while (isdigit(EmitCharAndAdvance(tokens, *at_))) { } ExponentAndKind(tokens); + } else if (nch == '.' && EmitCharAndAdvance(tokens, '.') == '.') { + EmitCharAndAdvance(tokens, '.'); // variadic macro definition ellipsis } preventHollerith_ = false; } else if (IsNameChar(*at_)) {