Commit 4358cb2f by Dino Radakovic Committed by Copybara-Service

`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
parent df2c771e
...@@ -579,7 +579,7 @@ static bool ParseUnresolvedName(State *state); ...@@ -579,7 +579,7 @@ static bool ParseUnresolvedName(State *state);
static bool ParseExpression(State *state); static bool ParseExpression(State *state);
static bool ParseExprPrimary(State *state); static bool ParseExprPrimary(State *state);
static bool ParseExprCastValue(State *state); static bool ParseExprCastValue(State *state);
static bool ParseRequiresClauseExpression(State *state); static bool ParseQRequiresClauseExpr(State *state);
static bool ParseLocalName(State *state); static bool ParseLocalName(State *state);
static bool ParseLocalNameSuffix(State *state); static bool ParseLocalNameSuffix(State *state);
static bool ParseDiscriminator(State *state); static bool ParseDiscriminator(State *state);
...@@ -646,12 +646,8 @@ static bool ParseEncoding(State *state) { ...@@ -646,12 +646,8 @@ static bool ParseEncoding(State *state) {
// Parsed: <(function) name> <bare-function-type> // Parsed: <(function) name> <bare-function-type>
// Pending: [`Q` <requires-clause expr>] // Pending: [`Q` <requires-clause expr>]
ParseState copy = state->parse_state; ParseQRequiresClauseExpr(state); // restores state on failure
if (ParseOneCharToken(state, 'Q') && ParseRequiresClauseExpression(state)) { return true;
return true; // <(function) name> <bare-function-type> `Q` <requires>
}
state->parse_state = copy;
return true; // <(function) name> <bare-function-type>
} }
if (ParseSpecialName(state)) { if (ParseSpecialName(state)) {
...@@ -1505,16 +1501,14 @@ static bool ParseTemplateTemplateParam(State *state) { ...@@ -1505,16 +1501,14 @@ static bool ParseTemplateTemplateParam(State *state) {
ParseSubstitution(state, /*accept_std=*/false)); ParseSubstitution(state, /*accept_std=*/false));
} }
// <template-args> ::= I <template-arg>+ E // <template-args> ::= I <template-arg>+ [Q <requires-clause expr>] E
//
// TODO(b/324066279): Implement optional [Q <requires-clause expr>] before E.
// See: http://shortn/_Z7yM7PonSD
static bool ParseTemplateArgs(State *state) { static bool ParseTemplateArgs(State *state) {
ComplexityGuard guard(state); ComplexityGuard guard(state);
if (guard.IsTooComplex()) return false; if (guard.IsTooComplex()) return false;
ParseState copy = state->parse_state; ParseState copy = state->parse_state;
DisableAppend(state); DisableAppend(state);
if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) &&
Optional(ParseQRequiresClauseExpr(state)) &&
ParseOneCharToken(state, 'E')) { ParseOneCharToken(state, 'E')) {
RestoreAppend(state, copy.append); RestoreAppend(state, copy.append);
MaybeAppend(state, "<>"); MaybeAppend(state, "<>");
...@@ -1930,7 +1924,12 @@ static bool ParseExprCastValue(State *state) { ...@@ -1930,7 +1924,12 @@ static bool ParseExprCastValue(State *state) {
return false; 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. // Does not emit the parsed `requires` clause to simplify the implementation.
// In other words, these two functions' mangled names will demangle identically: // In other words, these two functions' mangled names will demangle identically:
...@@ -1942,12 +1941,19 @@ static bool ParseExprCastValue(State *state) { ...@@ -1942,12 +1941,19 @@ static bool ParseExprCastValue(State *state) {
// //
// template <typename T> // template <typename T>
// int foo(T); // int foo(T);
static bool ParseRequiresClauseExpression(State *state) { static bool ParseQRequiresClauseExpr(State *state) {
bool original_append = state->parse_state.append; ParseState copy = state->parse_state;
DisableAppend(state); DisableAppend(state);
bool result = ParseExpression(state);
RestoreAppend(state, original_append); // <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8
return result; 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>] // <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]
......
...@@ -75,6 +75,42 @@ TEST(Demangle, FunctionTemplateWithFunctionRequiresClause) { ...@@ -75,6 +75,42 @@ TEST(Demangle, FunctionTemplateWithFunctionRequiresClause) {
EXPECT_STREQ(tmp, "foo<>()"); 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) { TEST(Demangle, FunctionTemplateWithAutoParam) {
char tmp[100]; char tmp[100];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment