diff options
author | Dino Radakovic <dinor@google.com> | 2024-02-09 18:46:09 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2024-02-09 18:46:50 -0800 |
commit | 4358cb2f8cb304e64d9a2d2845f472297724e19f (patch) | |
tree | 524ff2f5c3c6b5325f7e013b28aee1c879276e4a | |
parent | df2c771ec596b385448117f237ee70be35efe4ce (diff) |
`demangle`: Parse `requires` clauses on template params, before function return type
For example, this covers the following:
```
template <typename T>
requires std::integral<T>
int foo();
```
Refactor parsing of `Q <requires-clause expr>` into a single function that performs backtracking to avoid reimplementing `ParseTemplateArgs` in terms of nested if-else blocks.
PiperOrigin-RevId: 605785418
Change-Id: I118998a75e050dcf46af125b613b690312fd3cbe
-rw-r--r-- | absl/debugging/internal/demangle.cc | 40 | ||||
-rw-r--r-- | absl/debugging/internal/demangle_test.cc | 36 |
2 files changed, 59 insertions, 17 deletions
diff --git a/absl/debugging/internal/demangle.cc b/absl/debugging/internal/demangle.cc index c2f19451..f86421bf 100644 --- a/absl/debugging/internal/demangle.cc +++ b/absl/debugging/internal/demangle.cc @@ -579,7 +579,7 @@ static bool ParseUnresolvedName(State *state); static bool ParseExpression(State *state); static bool ParseExprPrimary(State *state); static bool ParseExprCastValue(State *state); -static bool ParseRequiresClauseExpression(State *state); +static bool ParseQRequiresClauseExpr(State *state); static bool ParseLocalName(State *state); static bool ParseLocalNameSuffix(State *state); static bool ParseDiscriminator(State *state); @@ -646,12 +646,8 @@ static bool ParseEncoding(State *state) { // Parsed: <(function) name> <bare-function-type> // Pending: [`Q` <requires-clause expr>] - ParseState copy = state->parse_state; - if (ParseOneCharToken(state, 'Q') && ParseRequiresClauseExpression(state)) { - return true; // <(function) name> <bare-function-type> `Q` <requires> - } - state->parse_state = copy; - return true; // <(function) name> <bare-function-type> + ParseQRequiresClauseExpr(state); // restores state on failure + return true; } if (ParseSpecialName(state)) { @@ -1505,16 +1501,14 @@ static bool ParseTemplateTemplateParam(State *state) { ParseSubstitution(state, /*accept_std=*/false)); } -// <template-args> ::= I <template-arg>+ E -// -// TODO(b/324066279): Implement optional [Q <requires-clause expr>] before E. -// See: http://shortn/_Z7yM7PonSD +// <template-args> ::= I <template-arg>+ [Q <requires-clause expr>] E static bool ParseTemplateArgs(State *state) { ComplexityGuard guard(state); if (guard.IsTooComplex()) return false; ParseState copy = state->parse_state; DisableAppend(state); if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && + Optional(ParseQRequiresClauseExpr(state)) && ParseOneCharToken(state, 'E')) { RestoreAppend(state, copy.append); MaybeAppend(state, "<>"); @@ -1930,7 +1924,12 @@ static bool ParseExprCastValue(State *state) { return false; } -// <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8 +// Parses `Q <requires-clause expr>`. +// If parsing fails, applies backtracking to `state`. +// +// This function covers two symbols instead of one for convenience, +// because in LLVM's Itanium ABI mangling grammar, <requires-clause expr> +// always appears after Q. // // Does not emit the parsed `requires` clause to simplify the implementation. // In other words, these two functions' mangled names will demangle identically: @@ -1942,12 +1941,19 @@ static bool ParseExprCastValue(State *state) { // // template <typename T> // int foo(T); -static bool ParseRequiresClauseExpression(State *state) { - bool original_append = state->parse_state.append; +static bool ParseQRequiresClauseExpr(State *state) { + ParseState copy = state->parse_state; DisableAppend(state); - bool result = ParseExpression(state); - RestoreAppend(state, original_append); - return result; + + // <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8 + if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) { + RestoreAppend(state, copy.append); + return true; + } + + // also restores append + state->parse_state = copy; + return false; } // <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>] diff --git a/absl/debugging/internal/demangle_test.cc b/absl/debugging/internal/demangle_test.cc index 4b9d7711..86e66886 100644 --- a/absl/debugging/internal/demangle_test.cc +++ b/absl/debugging/internal/demangle_test.cc @@ -75,6 +75,42 @@ TEST(Demangle, FunctionTemplateWithFunctionRequiresClause) { EXPECT_STREQ(tmp, "foo<>()"); } +TEST(Demangle, FunctionWithTemplateParamRequiresClause) { + char tmp[100]; + + // template <typename T> + // requires std::integral<T> + // int foo(); + // + // foo<int>(); + ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEiv", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "foo<>()"); +} + +TEST(Demangle, FunctionWithTemplateParamAndFunctionRequiresClauses) { + char tmp[100]; + + // template <typename T> + // requires std::integral<T> + // int foo() requires std::integral<T>; + // + // foo<int>(); + ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEivQsr3stdE8integralIS0_E", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "foo<>()"); +} + +TEST(Demangle, FunctionTemplateBacktracksOnMalformedRequiresClause) { + char tmp[100]; + + // template <typename T> + // int foo(T); + // + // foo<int>(5); + // Except there's an extra `Q` where the mangled requires clause would be. + ASSERT_FALSE(Demangle("_Z3fooIiQEiT_", tmp, sizeof(tmp))); +} + TEST(Demangle, FunctionTemplateWithAutoParam) { char tmp[100]; |