From 4358cb2f8cb304e64d9a2d2845f472297724e19f Mon Sep 17 00:00:00 2001 From: Dino Radakovic Date: Fri, 9 Feb 2024 18:46:09 -0800 Subject: `demangle`: Parse `requires` clauses on template params, before function return type For example, this covers the following: ``` template requires std::integral int foo(); ``` Refactor parsing of `Q ` into a single function that performs backtracking to avoid reimplementing `ParseTemplateArgs` in terms of nested if-else blocks. PiperOrigin-RevId: 605785418 Change-Id: I118998a75e050dcf46af125b613b690312fd3cbe --- absl/debugging/internal/demangle.cc | 40 ++++++++++++++++++-------------- absl/debugging/internal/demangle_test.cc | 36 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 17 deletions(-) (limited to 'absl') 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> // Pending: [`Q` ] - ParseState copy = state->parse_state; - if (ParseOneCharToken(state, 'Q') && ParseRequiresClauseExpression(state)) { - return true; // <(function) name> `Q` - } - state->parse_state = copy; - return true; // <(function) name> + 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)); } -// ::= I + E -// -// TODO(b/324066279): Implement optional [Q ] before E. -// See: http://shortn/_Z7yM7PonSD +// ::= I + [Q ] 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; } -// is just an : http://shortn/_9E1Ul0rIM8 +// Parses `Q `. +// 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, +// 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 // 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; + + // is just an : http://shortn/_9E1Ul0rIM8 + if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) { + RestoreAppend(state, copy.append); + return true; + } + + // also restores append + state->parse_state = copy; + return false; } // ::= Z <(function) encoding> E <(entity) name> [] 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 + // requires std::integral + // int foo(); + // + // foo(); + ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEiv", tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "foo<>()"); +} + +TEST(Demangle, FunctionWithTemplateParamAndFunctionRequiresClauses) { + char tmp[100]; + + // template + // requires std::integral + // int foo() requires std::integral; + // + // foo(); + ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEivQsr3stdE8integralIS0_E", + tmp, sizeof(tmp))); + EXPECT_STREQ(tmp, "foo<>()"); +} + +TEST(Demangle, FunctionTemplateBacktracksOnMalformedRequiresClause) { + char tmp[100]; + + // template + // int foo(T); + // + // foo(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]; -- cgit v1.2.3