In PR 59195, I describe a demangler problem with parsing conversion operators that have template parameters:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59195 For example, $ c++filt _ZN1AcvPT_I1CEEv fails -- it should print "A::operator C*<C>()". $ c++filt _ZN1AcvT_IiEI1CEEv prints "A::operator int<int><C>()" instead of "A::operator C<int><C>()". This patch fixes the parser to resolve the ambiguity in the mangled name grammar where it can't tell if the "T_" is a <template-param> or is the start of <template-template-param> <template-args>, and fixes the printer to use the correct context when resolving the template parameter substitution. Passes all regression tests, and I've added new test cases. OK to commit? -cary 2013-11-19 Cary Coutant <ccout...@google.com> libiberty/ PR other/59195 * cp-demangle.c (struct d_info_checkpoint): New struct. (struct d_print_info): Add current_template field. (d_operator_name): Set flag when processing a conversion operator. (cplus_demangle_type): When processing <template-args> for a conversion operator, backtrack if necessary. (d_expression_1): Renamed from d_expression. (d_expression): New wrapper around d_expression_1. (d_checkpoint): New function. (d_backtrack): New function. (d_print_init): Initialize current_template. (d_print_comp): Set current_template. (d_print_cast): Put current_template in scope for printing conversion operator name. (cplus_demangle_init_info): Initialize is_expression and is_conversion. * cp-demangle.h (struct d_info): Add is_expression and is_conversion fields. * testsuite/demangle-expected: New test cases. diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c index 7be9804..edfd85c 100644 --- a/libiberty/cp-demangle.c +++ b/libiberty/cp-demangle.c @@ -287,6 +287,19 @@ struct d_saved_scope struct d_print_template *templates; }; +/* Checkpoint structure to allow backtracking. This holds copies + of the fields of struct d_info that need to be restored + if a trial parse needs to be backtracked over. */ + +struct d_info_checkpoint +{ + const char *n; + int next_comp; + int next_sub; + int did_subs; + int expansion; +}; + enum { D_PRINT_BUFFER_LENGTH = 256 }; struct d_print_info { @@ -318,6 +331,8 @@ struct d_print_info struct d_saved_scope *saved_scopes; /* Number of saved scopes in the above array. */ int num_saved_scopes; + /* The nearest enclosing template, if any. */ + const struct demangle_component *current_template; }; #ifdef CP_DEMANGLE_DEBUG @@ -444,6 +459,10 @@ d_add_substitution (struct d_info *, struct demangle_component *); static struct demangle_component *d_substitution (struct d_info *, int); +static void d_checkpoint (struct d_info *, struct d_info_checkpoint *); + +static void d_backtrack (struct d_info *, struct d_info_checkpoint *); + static void d_growable_string_init (struct d_growable_string *, size_t); static inline void @@ -1734,8 +1753,15 @@ d_operator_name (struct d_info *di) if (c1 == 'v' && IS_DIGIT (c2)) return d_make_extended_operator (di, c2 - '0', d_source_name (di)); else if (c1 == 'c' && c2 == 'v') - return d_make_comp (di, DEMANGLE_COMPONENT_CAST, - cplus_demangle_type (di), NULL); + { + struct demangle_component *type; + + if (!di->is_expression) + di->is_conversion = 1; + type = cplus_demangle_type (di); + di->is_conversion = 0; + return d_make_comp (di, DEMANGLE_COMPONENT_CAST, type, NULL); + } else { /* LOW is the inclusive lower bound. */ @@ -2284,13 +2310,61 @@ cplus_demangle_type (struct d_info *di) ret = d_template_param (di); if (d_peek_char (di) == 'I') { - /* This is <template-template-param> <template-args>. The - <template-template-param> part is a substitution + /* This may be <template-template-param> <template-args>. + If this is the type for a conversion operator, we can + have a <template-template-param> here only by following + a derivation like this: + + <nested-name> + -> <template-prefix> <template-args> + -> <prefix> <template-unqualified-name> <template-args> + -> <unqualified-name> <template-unqualified-name> <template-args> + -> <source-name> <template-unqualified-name> <template-args> + -> <source-name> <operator-name> <template-args> + -> <source-name> cv <type> <template-args> + -> <source-name> cv <template-template-param> <template-args> <template-args> + + where the <template-args> is followed by another. + Otherwise, we must have a derivation like this: + + <nested-name> + -> <template-prefix> <template-args> + -> <prefix> <template-unqualified-name> <template-args> + -> <unqualified-name> <template-unqualified-name> <template-args> + -> <source-name> <template-unqualified-name> <template-args> + -> <source-name> <operator-name> <template-args> + -> <source-name> cv <type> <template-args> + -> <source-name> cv <template-param> <template-args> + + where we need to leave the <template-args> to be processed + by d_prefix (following the <template-prefix>). + + The <template-template-param> part is a substitution candidate. */ - if (! d_add_substitution (di, ret)) - return NULL; - ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, - d_template_args (di)); + if (! di->is_conversion) + { + if (! d_add_substitution (di, ret)) + return NULL; + ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, + d_template_args (di)); + } + else + { + struct demangle_component *args; + struct d_info_checkpoint checkpoint; + + d_checkpoint (di, &checkpoint); + args = d_template_args (di); + if (d_peek_char (di) == 'I') + { + if (! d_add_substitution (di, ret)) + return NULL; + ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, + args); + } + else + d_backtrack (di, &checkpoint); + } } break; @@ -2976,8 +3050,8 @@ op_is_new_cast (struct demangle_component *op) ::= <expr-primary> */ -static struct demangle_component * -d_expression (struct d_info *di) +static inline struct demangle_component * +d_expression_1 (struct d_info *di) { char peek; @@ -3203,6 +3277,17 @@ d_expression (struct d_info *di) } } +static struct demangle_component * +d_expression (struct d_info *di) +{ + struct demangle_component *ret; + + di->is_expression = 1; + ret = d_expression_1 (di); + di->is_expression = 0; + return ret; +} + /* <expr-primary> ::= L <type> <(value) number> E ::= L <type> <(value) float> E ::= L <mangled-name> E @@ -3588,6 +3673,26 @@ d_substitution (struct d_info *di, int prefix) } } +static void +d_checkpoint (struct d_info *di, struct d_info_checkpoint *checkpoint) +{ + checkpoint->n = di->n; + checkpoint->next_comp = di->next_comp; + checkpoint->next_sub = di->next_sub; + checkpoint->did_subs = di->did_subs; + checkpoint->expansion = di->expansion; +} + +static void +d_backtrack (struct d_info *di, struct d_info_checkpoint *checkpoint) +{ + di->n = checkpoint->n; + di->next_comp = checkpoint->next_comp; + di->next_sub = checkpoint->next_sub; + di->did_subs = checkpoint->did_subs; + di->expansion = checkpoint->expansion; +} + /* Initialize a growable string. */ static void @@ -3684,6 +3789,7 @@ d_print_init (struct d_print_info *dpi, demangle_callbackref callback, dpi->saved_scopes = NULL; dpi->num_saved_scopes = 0; + dpi->current_template = NULL; } /* Free a print information structure. */ @@ -4165,6 +4271,12 @@ d_print_comp (struct d_print_info *dpi, int options, { struct d_print_mod *hold_dpm; struct demangle_component *dcl; + const struct demangle_component *hold_current; + + /* This template may need to be referenced by a cast operator + contained in its subtree. */ + hold_current = dpi->current_template; + dpi->current_template = dc; /* Don't push modifiers into a template definition. Doing so could give the wrong definition for a template argument. @@ -4201,6 +4313,7 @@ d_print_comp (struct d_print_info *dpi, int options, } dpi->modifiers = hold_dpm; + dpi->current_template = hold_current; return; } @@ -5416,28 +5529,32 @@ static void d_print_cast (struct d_print_info *dpi, int options, const struct demangle_component *dc) { - if (d_left (dc)->type != DEMANGLE_COMPONENT_TEMPLATE) - d_print_comp (dpi, options, d_left (dc)); - else - { - struct d_print_mod *hold_dpm; - struct d_print_template dpt; - - /* It appears that for a templated cast operator, we need to put - the template parameters in scope for the operator name, but - not for the parameters. The effect is that we need to handle - the template printing here. */ - - hold_dpm = dpi->modifiers; - dpi->modifiers = NULL; + struct d_print_template dpt; + /* For a cast operator, we need the template parameters from + the enclosing template in scope for processing the type. */ + if (dpi->current_template != NULL) + { dpt.next = dpi->templates; dpi->templates = &dpt; - dpt.template_decl = d_left (dc); + dpt.template_decl = dpi->current_template; + } + if (d_left (dc)->type != DEMANGLE_COMPONENT_TEMPLATE) + { + d_print_comp (dpi, options, d_left (dc)); + if (dpi->current_template != NULL) + dpi->templates = dpt.next; + } + else + { d_print_comp (dpi, options, d_left (d_left (dc))); - dpi->templates = dpt.next; + /* For a templated cast operator, we need to remove the template + parameters from scope after printing the operator name, + so we need to handle the template printing here. */ + if (dpi->current_template != NULL) + dpi->templates = dpt.next; if (d_last_char (dpi) == '<') d_append_char (dpi, ' '); @@ -5448,8 +5565,6 @@ d_print_cast (struct d_print_info *dpi, int options, if (d_last_char (dpi) == '>') d_append_char (dpi, ' '); d_append_char (dpi, '>'); - - dpi->modifiers = hold_dpm; } } @@ -5482,6 +5597,8 @@ cplus_demangle_init_info (const char *mangled, int options, size_t len, di->last_name = NULL; di->expansion = 0; + di->is_expression = 0; + di->is_conversion = 0; } /* Internal implementation for the demangler. If MANGLED is a g++ v3 ABI diff --git a/libiberty/cp-demangle.h b/libiberty/cp-demangle.h index ae635be..6fce025 100644 --- a/libiberty/cp-demangle.h +++ b/libiberty/cp-demangle.h @@ -122,6 +122,11 @@ struct d_info mangled name to the demangled name, such as standard substitutions and builtin types. */ int expansion; + /* Non-zero if we are parsing an expression. */ + int is_expression; + /* Non-zero if we are parsing the type operand of a conversion + operator, but not when in an expression. */ + int is_conversion; }; /* To avoid running past the ending '\0', don't: diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected index de03bfa..3ff08e6 100644 --- a/libiberty/testsuite/demangle-expected +++ b/libiberty/testsuite/demangle-expected @@ -4304,6 +4304,16 @@ strings::internal::Splitter<strings::delimiter::AnyOf, strings::SkipEmpty>::oper strings::internal::Splitter<strings::delimiter::AnyOf, strings::SkipEmpty>::operator std::vector<basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<basic_string<char, std::char_traits<char>, std::allocator<char> > > ><std::vector<basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<basic_string<char, std::char_traits<char>, std::allocator<char> > > >, void> # --format=gnu-v3 --no-params -_ZN1AIiEcvPT_I1CEEv -A<int>::operator C<C>*() -A<int>::operator C<C>* +_ZN1AcvT_I1CEEv +A::operator C<C>() +A::operator C<C> +# +--format=gnu-v3 --no-params +_ZN1AcvPT_I1CEEv +A::operator C*<C>() +A::operator C*<C> +# +--format=gnu-v3 --no-params +_ZN1AcvT_IiEI1CEEv +A::operator C<int><C>() +A::operator C<int><C>