Skip to content

Commit 090a0bc

Browse files
dinordnetkex
authored andcommitted
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
1 parent ae14c61 commit 090a0bc

File tree

2 files changed

+59
-17
lines changed

2 files changed

+59
-17
lines changed

absl/debugging/internal/demangle.cc

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ static bool ParseUnresolvedName(State *state);
579579
static bool ParseExpression(State *state);
580580
static bool ParseExprPrimary(State *state);
581581
static bool ParseExprCastValue(State *state);
582-
static bool ParseRequiresClauseExpression(State *state);
582+
static bool ParseQRequiresClauseExpr(State *state);
583583
static bool ParseLocalName(State *state);
584584
static bool ParseLocalNameSuffix(State *state);
585585
static bool ParseDiscriminator(State *state);
@@ -646,12 +646,8 @@ static bool ParseEncoding(State *state) {
646646

647647
// Parsed: <(function) name> <bare-function-type>
648648
// Pending: [`Q` <requires-clause expr>]
649-
ParseState copy = state->parse_state;
650-
if (ParseOneCharToken(state, 'Q') && ParseRequiresClauseExpression(state)) {
651-
return true; // <(function) name> <bare-function-type> `Q` <requires>
652-
}
653-
state->parse_state = copy;
654-
return true; // <(function) name> <bare-function-type>
649+
ParseQRequiresClauseExpr(state); // restores state on failure
650+
return true;
655651
}
656652

657653
if (ParseSpecialName(state)) {
@@ -1505,16 +1501,14 @@ static bool ParseTemplateTemplateParam(State *state) {
15051501
ParseSubstitution(state, /*accept_std=*/false));
15061502
}
15071503

1508-
// <template-args> ::= I <template-arg>+ E
1509-
//
1510-
// TODO(b/324066279): Implement optional [Q <requires-clause expr>] before E.
1511-
// See: http://shortn/_Z7yM7PonSD
1504+
// <template-args> ::= I <template-arg>+ [Q <requires-clause expr>] E
15121505
static bool ParseTemplateArgs(State *state) {
15131506
ComplexityGuard guard(state);
15141507
if (guard.IsTooComplex()) return false;
15151508
ParseState copy = state->parse_state;
15161509
DisableAppend(state);
15171510
if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) &&
1511+
Optional(ParseQRequiresClauseExpr(state)) &&
15181512
ParseOneCharToken(state, 'E')) {
15191513
RestoreAppend(state, copy.append);
15201514
MaybeAppend(state, "<>");
@@ -1930,7 +1924,12 @@ static bool ParseExprCastValue(State *state) {
19301924
return false;
19311925
}
19321926

1933-
// <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8
1927+
// Parses `Q <requires-clause expr>`.
1928+
// If parsing fails, applies backtracking to `state`.
1929+
//
1930+
// This function covers two symbols instead of one for convenience,
1931+
// because in LLVM's Itanium ABI mangling grammar, <requires-clause expr>
1932+
// always appears after Q.
19341933
//
19351934
// Does not emit the parsed `requires` clause to simplify the implementation.
19361935
// In other words, these two functions' mangled names will demangle identically:
@@ -1942,12 +1941,19 @@ static bool ParseExprCastValue(State *state) {
19421941
//
19431942
// template <typename T>
19441943
// int foo(T);
1945-
static bool ParseRequiresClauseExpression(State *state) {
1946-
bool original_append = state->parse_state.append;
1944+
static bool ParseQRequiresClauseExpr(State *state) {
1945+
ParseState copy = state->parse_state;
19471946
DisableAppend(state);
1948-
bool result = ParseExpression(state);
1949-
RestoreAppend(state, original_append);
1950-
return result;
1947+
1948+
// <requires-clause expr> is just an <expression>: http://shortn/_9E1Ul0rIM8
1949+
if (ParseOneCharToken(state, 'Q') && ParseExpression(state)) {
1950+
RestoreAppend(state, copy.append);
1951+
return true;
1952+
}
1953+
1954+
// also restores append
1955+
state->parse_state = copy;
1956+
return false;
19511957
}
19521958

19531959
// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>]

absl/debugging/internal/demangle_test.cc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,42 @@ TEST(Demangle, FunctionTemplateWithFunctionRequiresClause) {
7575
EXPECT_STREQ(tmp, "foo<>()");
7676
}
7777

78+
TEST(Demangle, FunctionWithTemplateParamRequiresClause) {
79+
char tmp[100];
80+
81+
// template <typename T>
82+
// requires std::integral<T>
83+
// int foo();
84+
//
85+
// foo<int>();
86+
ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEiv", tmp, sizeof(tmp)));
87+
EXPECT_STREQ(tmp, "foo<>()");
88+
}
89+
90+
TEST(Demangle, FunctionWithTemplateParamAndFunctionRequiresClauses) {
91+
char tmp[100];
92+
93+
// template <typename T>
94+
// requires std::integral<T>
95+
// int foo() requires std::integral<T>;
96+
//
97+
// foo<int>();
98+
ASSERT_TRUE(Demangle("_Z3fooIiQsr3stdE8integralIT_EEivQsr3stdE8integralIS0_E",
99+
tmp, sizeof(tmp)));
100+
EXPECT_STREQ(tmp, "foo<>()");
101+
}
102+
103+
TEST(Demangle, FunctionTemplateBacktracksOnMalformedRequiresClause) {
104+
char tmp[100];
105+
106+
// template <typename T>
107+
// int foo(T);
108+
//
109+
// foo<int>(5);
110+
// Except there's an extra `Q` where the mangled requires clause would be.
111+
ASSERT_FALSE(Demangle("_Z3fooIiQEiT_", tmp, sizeof(tmp)));
112+
}
113+
78114
TEST(Demangle, FunctionTemplateWithAutoParam) {
79115
char tmp[100];
80116

0 commit comments

Comments
 (0)