Message ID | 20180109215111.GA23312@redhat.com |
---|---|
State | New |
Headers | show |
Series | PR libstdc++/80276 fix pretty printers for array smart pointers | expand |
On 09/01/18 21:51 +0000, Jonathan Wakely wrote: >This fixes unique_ptr<T[]> so it doesn't print as unique_ptr<T>, and >fixes a problem in the type-printers that can result in a gdb.error >exception (see https://sourceware.org/PR22686). > > PR libstdc++/80276 > * python/libstdcxx/v6/printers.py (SharedPointerPrinter) > (UniquePointerPrinter): Print correct template argument, not type of > the pointer. > (TemplateTypePrinter._recognizer.recognize): Handle failure to lookup > a type. > * testsuite/libstdc++-prettyprinters/cxx11.cc: Test unique_ptr of > array type. > * testsuite/libstdc++-prettyprinters/cxx17.cc: Test shared_ptr and > weak_ptr of array types. > >Tested x86_64-linux, committed to trunk. That patch fixed the unhandled gdb.error exception that got raised, but it just meant that the type printer gave up and stopped trying to pretty-print the template arguments. That meant if a template argument is a type that has a type printer, we don't process it. This patch rewrites the TemplateTypePrinter to use gdb.Type.template_arguments() and work directly with the gdb.Type objects for the template arguments, instead of using a regex to process strings. This means we don't have to use gdb.lookup_type() to convert strings to types, and so can handle arrays correctly. I also made the type printers handle the types defined in namespace std::__cxx11, which didn't work previously. There are two (IMHO acceptable) regressions with this change: basic_string<signed char> and basic_string<unsigned char> will no longer omit the default template arguments (because the type printers for those specializations conflict with the ones for std::string, std::wstring, std::u16string and std::u32string). I don't think this is a a big deal, because I doubt many people are using strings of signed/unsigned char. If necessary we could match those specializations with TemplateTypePrinter: for abi in ('', '__cxx11'): for ch in ('signed char', 'unsigned char'): string_type = abi + 'basic_string<' + ch add_one_template_type_printer(obj, string_type, { 1: 'std::char_traits<{0}>', 2: 'std::allocator<{0}>'}) But do we need type printers for these specializations? Secondly, std::experimental::optional no longer omits the fundamentals_v1 inline namespace ... but then it didn't work properly anyway because it displayed as "std::experimental::optional<\1>". So arguably it's better to have no type printer than one that displays the wrong type! I've also removed the type printer for std::experimental::any was bogus, because it tried to match a template, and any isn't a template! I've considered adding a new type of printer which doesn't do anything fancy, but just prints the type name (omitting any inline namespaces like "__8", "__cxx11", or "fundamentals_v1"). This would mean that GDB just shows a string like "type = std::any" instead of printing the class definition (you could still use "ptype/r for the full output). If nobody objects to these regressions I'll commit this to trunk. commit 485fa83d669bd813a1e1f09162415453f510a1b6 Author: Jonathan Wakely <jwakely@redhat.com> Date: Thu Jan 11 16:25:05 2018 +0000 PR libstdc++/80276 fix template argument handling in type printers PR libstdc++/80276 * python/libstdcxx/v6/printers.py (strip_inline_namespaces): New. (get_template_arg_list): New. (StdVariantPrinter._template_args): Remove, use get_template_arg_list instead. (TemplateTypePrinter): Rewrite to work with gdb.Type objects instead of strings and regular expressions. (add_one_template_type_printer): Adapt to new TemplateTypePrinter. (FilteringTypePrinter): Add docstring. Match using startswith. Use strip_inline_namespaces instead of strip_versioned_namespace. (add_one_type_printer): Prepend namespace to match argument. (register_type_printers): Add type printers for char16_t and char32_t string types and for types using cxx11 ABI. Update calls to add_one_template_type_printer to provide default argument dicts. * testsuite/libstdc++-prettyprinters/80276.cc: New test. * testsuite/libstdc++-prettyprinters/whatis.cc: Remove tests for basic_string<unsigned char> and basic_string<signed char>. * testsuite/libstdc++-prettyprinters/whatis2.cc: Duplicate whatis.cc to test local variables, without overriding _GLIBCXX_USE_CXX11_ABI. diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index d6de0969e1c..c490880cd10 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -101,8 +101,8 @@ def find_type(orig, name): _versioned_namespace = '__8::' -# Test if a type is a given template instantiation. def is_specialization_of(type, template_name): + "Test if a type is a given template instantiation." global _versioned_namespace if _versioned_namespace: return re.match('^std::(%s)?%s<.*>$' % (_versioned_namespace, template_name), type) is not None @@ -114,6 +114,28 @@ def strip_versioned_namespace(typename): return typename.replace(_versioned_namespace, '') return typename +def strip_inline_namespaces(type_str): + "Remove known inline namespaces from the canonical name of a type." + type_str = strip_versioned_namespace(type_str) + type_str = type_str.replace('std::__cxx11::', 'std::') + expt_ns = 'std::experimental::' + for lfts_ns in ('fundamentals_v1', 'fundamentals_v2'): + type_str = type_str.replace(expt_ns+lfts_ns+'::', expt_ns) + fs_ns = expt_ns + 'filesystem::' + type_str = type_str.replace(fs_ns+'v1::', fs_ns) + return type_str + +def get_template_arg_list(type_obj): + "Return a type's template arguments as a list" + n = 0 + template_args = [] + while True: + try: + template_args.append(type_obj.template_argument(n)) + except: + return template_args + n += 1 + class SmartPtrIterator(Iterator): "An iterator for smart pointer types with a single 'child' value" @@ -1063,7 +1085,7 @@ class StdVariantPrinter(SingleObjContainerPrinter): "Print a std::variant" def __init__(self, typename, val): - alternatives = self._template_args(val) + alternatives = get_template_arg_list(val.type) self.typename = strip_versioned_namespace(typename) self.typename = "%s<%s>" % (self.typename, ', '.join([self._recognize(alt) for alt in alternatives])) self.index = val['_M_index'] @@ -1078,17 +1100,6 @@ class StdVariantPrinter(SingleObjContainerPrinter): visualizer = gdb.default_visualizer(contained_value) super (StdVariantPrinter, self).__init__(contained_value, visualizer, 'array') - @staticmethod - def _template_args(val): - n = 0 - args = [] - while True: - try: - args.append(val.type.template_argument(n)) - except: - return args - n += 1 - def to_string(self): if self.contained_value is None: return "%s [no contained value]" % self.typename @@ -1294,84 +1305,168 @@ libstdcxx_printer = None class TemplateTypePrinter(object): r""" - A type printer for class templates. + A type printer for class templates with default template arguments. - Recognizes type names that match a regular expression. - Replaces them with a formatted string which can use replacement field - {N} to refer to the \N subgroup of the regex match. - Type printers are recusively applied to the subgroups. + Recognizes specializations of class templates and prints them without + any template arguments that use a default template argument. + Type printers are recursively applied to the template arguments. - This allows recognizing e.g. "std::vector<(.*), std::allocator<\\1> >" - and replacing it with "std::vector<{1}>", omitting the template argument - that uses the default type. + e.g. replace "std::vector<T, std::allocator<T> >" with "std::vector<T>". """ - def __init__(self, name, pattern, subst): + def __init__(self, name, defargs): self.name = name - self.pattern = re.compile(pattern) - self.subst = subst + self.defargs = defargs self.enabled = True class _recognizer(object): - def __init__(self, pattern, subst): - self.pattern = pattern - self.subst = subst - self.type_obj = None + "The recognizer class for TemplateTypePrinter." + + def __init__(self, name, defargs): + self.name = name + self.defargs = defargs + # self.type_obj = None def recognize(self, type_obj): + """ + If type_obj is a specialization of self.name that uses all the + default template arguments for the class template, then return + a string representation of the type without default arguments. + Otherwise, return None. + """ + if type_obj.tag is None: return None - m = self.pattern.match(type_obj.tag) - if m: - subs = list(m.groups()) - for i, sub in enumerate(subs): - if ('{%d}' % (i+1)) in self.subst: - # apply recognizers to subgroup - try: - subtype = gdb.lookup_type(sub) - except gdb.error: - continue - rep = gdb.types.apply_type_recognizers( - gdb.types.get_type_recognizers(), - subtype) - if rep: - subs[i] = rep - subs = [None] + subs - return self.subst.format(*subs) - return None + if not type_obj.tag.startswith(self.name): + return None + + template_args = get_template_arg_list(type_obj) + displayed_args = [] + require_defaulted = False + for n in range(len(template_args)): + # The actual template argument in the type: + targ = template_args[n] + # The default template argument for the class template: + defarg = self.defargs.get(n) + if defarg is not None: + # Substitute other template arguments into the default: + defarg = defarg.format(*template_args) + # Fail to recognize the type (by returning None) + # unless the actual argument is the same as the default. + try: + if targ != gdb.lookup_type(defarg): + return None + except gdb.error: + # Type lookup failed, just use string comparison: + if targ.tag != defarg: + return None + # All subsequent args must have defaults: + require_defaulted = True + elif require_defaulted: + return None + else: + # Recursively apply recognizers to the template argument + # and add it to the arguments that will be displayed: + displayed_args.append(self._recognize_subtype(targ)) + + # This assumes no class templates in the nested-name-specifier: + template_name = type_obj.tag[0:type_obj.tag.find('<')] + template_name = strip_inline_namespaces(template_name) + + return template_name + '<' + ', '.join(displayed_args) + '>' + + def _recognize_subtype(self, type_obj): + """Convert a gdb.Type to a string by applying recognizers, + or if that fails then simply converting to a string.""" + + if type_obj.code == gdb.TYPE_CODE_PTR: + return self._recognize_subtype(type_obj.target()) + '*' + if type_obj.code == gdb.TYPE_CODE_ARRAY: + type_str = self._recognize_subtype(type_obj.target()) + if str(type_obj.strip_typedefs()).endswith('[]'): + return type_str + '[]' # array of unknown bound + return "%s[%d]" % (type_str, type_obj.range()[1] + 1) + if type_obj.code == gdb.TYPE_CODE_REF: + return self._recognize_subtype(type_obj.target()) + '&' + if hasattr(gdb, 'TYPE_CODE_RVALUE_REF'): + if type_obj.code == gdb.TYPE_CODE_RVALUE_REF: + return self._recognize_subtype(type_obj.target()) + '&&' + + type_str = gdb.types.apply_type_recognizers( + gdb.types.get_type_recognizers(), type_obj) + if type_str: + return type_str + return str(type_obj) def instantiate(self): - return self._recognizer(self.pattern, self.subst) + "Return a recognizer object for this type printer." + return self._recognizer(self.name, self.defargs) -def add_one_template_type_printer(obj, name, match, subst): - match = '^std::' + match + '$' - printer = TemplateTypePrinter(name, match, 'std::' + subst) +def add_one_template_type_printer(obj, name, defargs): + r""" + Add a type printer for a class template with default template arguments. + + Args: + name (str): The template-name of the class template. + defargs (dict int:string) The default template arguments. + + Types in defargs can refer to the Nth template-argument using {N} + (with zero-based indices). + + e.g. 'unordered_map' has these defargs: + { 2: 'std::hash<{0}>', + 3: 'std::equal_to<{0}>', + 4: 'std::allocator<std::pair<const {0}, {1}> >' } + + """ + printer = TemplateTypePrinter('std::'+name, defargs) gdb.types.register_type_printer(obj, printer) if _versioned_namespace: # Add second type printer for same type in versioned namespace: - match = match.replace('std::', 'std::' + _versioned_namespace) - printer = TemplateTypePrinter(name, match, 'std::' + subst) + ns = 'std::' + _versioned_namespace + defargs = { n: d.replace('std::', ns) for n,d in defargs.items() } + printer = TemplateTypePrinter(ns+name, defargs) gdb.types.register_type_printer(obj, printer) class FilteringTypePrinter(object): + r""" + A type printer that uses typedef names for common template specializations. + + Args: + match (str): The class template to recognize. + name (str): The typedef-name that will be used instead. + + Checks if a specialization of the class template 'match' is the same type + as the typedef 'name', and prints it as 'name' instead. + + e.g. if an instantiation of std::basic_istream<C, T> is the same type as + std::istream then print it as std::istream. + """ + def __init__(self, match, name): self.match = match self.name = name self.enabled = True class _recognizer(object): + "The recognizer class for TemplateTypePrinter." + def __init__(self, match, name): self.match = match self.name = name self.type_obj = None def recognize(self, type_obj): + """ + If type_obj starts with self.match and is the same type as + self.name then return self.name, otherwise None. + """ if type_obj.tag is None: return None if self.type_obj is None: - if not self.match in type_obj.tag: + if not type_obj.tag.startswith(self.match): # Filter didn't match. return None try: @@ -1379,17 +1474,19 @@ class FilteringTypePrinter(object): except: pass if self.type_obj == type_obj: - return strip_versioned_namespace(self.name) + return strip_inline_namespaces(self.name) return None def instantiate(self): + "Return a recognizer object for this type printer." return self._recognizer(self.match, self.name) def add_one_type_printer(obj, match, name): - printer = FilteringTypePrinter(match, 'std::' + name) + printer = FilteringTypePrinter('std::' + match, 'std::' + name) gdb.types.register_type_printer(obj, printer) if _versioned_namespace: - printer = FilteringTypePrinter(match, 'std::' + _versioned_namespace + name) + ns = 'std::' + _versioned_namespace + printer = FilteringTypePrinter(ns + match, ns + name) gdb.types.register_type_printer(obj, printer) def register_type_printers(obj): @@ -1398,50 +1495,43 @@ def register_type_printers(obj): if not _use_type_printing: return - for pfx in ('', 'w'): - add_one_type_printer(obj, 'basic_string', pfx + 'string') - add_one_type_printer(obj, 'basic_string_view', pfx + 'string_view') - add_one_type_printer(obj, 'basic_ios', pfx + 'ios') - add_one_type_printer(obj, 'basic_streambuf', pfx + 'streambuf') - add_one_type_printer(obj, 'basic_istream', pfx + 'istream') - add_one_type_printer(obj, 'basic_ostream', pfx + 'ostream') - add_one_type_printer(obj, 'basic_iostream', pfx + 'iostream') - add_one_type_printer(obj, 'basic_stringbuf', pfx + 'stringbuf') - add_one_type_printer(obj, 'basic_istringstream', - pfx + 'istringstream') - add_one_type_printer(obj, 'basic_ostringstream', - pfx + 'ostringstream') - add_one_type_printer(obj, 'basic_stringstream', - pfx + 'stringstream') - add_one_type_printer(obj, 'basic_filebuf', pfx + 'filebuf') - add_one_type_printer(obj, 'basic_ifstream', pfx + 'ifstream') - add_one_type_printer(obj, 'basic_ofstream', pfx + 'ofstream') - add_one_type_printer(obj, 'basic_fstream', pfx + 'fstream') - add_one_type_printer(obj, 'basic_regex', pfx + 'regex') - add_one_type_printer(obj, 'sub_match', pfx + 'csub_match') - add_one_type_printer(obj, 'sub_match', pfx + 'ssub_match') - add_one_type_printer(obj, 'match_results', pfx + 'cmatch') - add_one_type_printer(obj, 'match_results', pfx + 'smatch') - add_one_type_printer(obj, 'regex_iterator', pfx + 'cregex_iterator') - add_one_type_printer(obj, 'regex_iterator', pfx + 'sregex_iterator') - add_one_type_printer(obj, 'regex_token_iterator', - pfx + 'cregex_token_iterator') - add_one_type_printer(obj, 'regex_token_iterator', - pfx + 'sregex_token_iterator') + # Add type printers for typedefs std::string, std::wstring etc. + for ch in ('', 'w', 'u16', 'u32'): + add_one_type_printer(obj, 'basic_string', ch + 'string') + add_one_type_printer(obj, '__cxx11::basic_string', + '__cxx11::' + ch + 'string') + add_one_type_printer(obj, 'basic_string_view', ch + 'string_view') + + # Add type printers for typedefs std::istream, std::wistream etc. + for ch in ('', 'w'): + for x in ('ios', 'streambuf', 'istream', 'ostream', 'iostream', + 'filebuf', 'ifstream', 'ofstream', 'fstream'): + add_one_type_printer(obj, 'basic_' + x, ch + x) + for x in ('stringbuf', 'istringstream', 'ostringstream', + 'stringstream'): + add_one_type_printer(obj, 'basic_' + x, ch + x) + # <sstream> types are in __cxx11 namespace, but typedefs aren'x: + add_one_type_printer(obj, '__cxx11::basic_' + x, ch + x) + + # Add type printers for typedefs regex, wregex, cmatch, wcmatch etc. + for abi in ('', '__cxx11::'): + for ch in ('', 'w'): + add_one_type_printer(obj, abi + 'basic_regex', abi + ch + 'regex') + for ch in ('c', 's', 'wc', 'ws'): + add_one_type_printer(obj, abi + 'match_results', abi + ch + 'match') + for x in ('sub_match', 'regex_iterator', 'regex_token_iterator'): + add_one_type_printer(obj, abi + x, abi + ch + x) # Note that we can't have a printer for std::wstreampos, because - # it shares the same underlying type as std::streampos. + # it is the same type as std::streampos. add_one_type_printer(obj, 'fpos', 'streampos') - add_one_type_printer(obj, 'basic_string', 'u16string') - add_one_type_printer(obj, 'basic_string', 'u32string') - add_one_type_printer(obj, 'basic_string_view', 'u16string_view') - add_one_type_printer(obj, 'basic_string_view', 'u32string_view') - + # Add type printers for <chrono> typedefs. for dur in ('nanoseconds', 'microseconds', 'milliseconds', 'seconds', 'minutes', 'hours'): add_one_type_printer(obj, 'duration', dur) + # Add type printers for <random> typedefs. add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand0') add_one_type_printer(obj, 'linear_congruential_engine', 'minstd_rand') add_one_type_printer(obj, 'mersenne_twister_engine', 'mt19937') @@ -1452,62 +1542,46 @@ def register_type_printers(obj): add_one_type_printer(obj, 'discard_block_engine', 'ranlux48') add_one_type_printer(obj, 'shuffle_order_engine', 'knuth_b') - # Do not show defaulted template arguments in class templates - add_one_template_type_printer(obj, 'unique_ptr<T>', - 'unique_ptr<(.*), std::default_delete<\\1 ?> >', - 'unique_ptr<{1}>') + # Add type printers for experimental::basic_string_view typedefs. + ns = 'experimental::fundamentals_v1::' + for ch in ('', 'w', 'u16', 'u32'): + add_one_type_printer(obj, ns + 'basic_string_view', + ns + ch + 'string_view') - add_one_template_type_printer(obj, 'basic_string<T>', - 'basic_string<((un)?signed char), std::char_traits<\\1 ?>, std::allocator<\\1 ?> >', - 'basic_string<{1}>') - - add_one_template_type_printer(obj, 'deque<T>', - 'deque<(.*), std::allocator<\\1 ?> >', - 'deque<{1}>') - add_one_template_type_printer(obj, 'forward_list<T>', - 'forward_list<(.*), std::allocator<\\1 ?> >', - 'forward_list<{1}>') - add_one_template_type_printer(obj, 'list<T>', - 'list<(.*), std::allocator<\\1 ?> >', - 'list<{1}>') - add_one_template_type_printer(obj, 'vector<T>', - 'vector<(.*), std::allocator<\\1 ?> >', - 'vector<{1}>') - add_one_template_type_printer(obj, 'map<Key, T>', - 'map<(.*), (.*), std::less<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', - 'map<{1}, {2}>') - add_one_template_type_printer(obj, 'multimap<Key, T>', - 'multimap<(.*), (.*), std::less<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', - 'multimap<{1}, {2}>') - add_one_template_type_printer(obj, 'set<T>', - 'set<(.*), std::less<\\1 ?>, std::allocator<\\1 ?> >', - 'set<{1}>') - add_one_template_type_printer(obj, 'multiset<T>', - 'multiset<(.*), std::less<\\1 ?>, std::allocator<\\1 ?> >', - 'multiset<{1}>') - add_one_template_type_printer(obj, 'unordered_map<Key, T>', - 'unordered_map<(.*), (.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', - 'unordered_map<{1}, {2}>') - add_one_template_type_printer(obj, 'unordered_multimap<Key, T>', - 'unordered_multimap<(.*), (.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<std::pair<\\1 const, \\2 ?> > >', - 'unordered_multimap<{1}, {2}>') - add_one_template_type_printer(obj, 'unordered_set<T>', - 'unordered_set<(.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<\\1 ?> >', - 'unordered_set<{1}>') - add_one_template_type_printer(obj, 'unordered_multiset<T>', - 'unordered_multiset<(.*), std::hash<\\1 ?>, std::equal_to<\\1 ?>, std::allocator<\\1 ?> >', - 'unordered_multiset<{1}>') - - # strip the "fundamentals_v1" inline namespace from these types - add_one_template_type_printer(obj, 'any<T>', - 'experimental::fundamentals_v\d::any<(.*)>', - 'experimental::any<\\1>') - add_one_template_type_printer(obj, 'optional<T>', - 'experimental::fundamentals_v\d::optional<(.*)>', - 'experimental::optional<\\1>') - add_one_template_type_printer(obj, 'basic_string_view<C>', - 'experimental::fundamentals_v\d::basic_string_view<(.*), std::char_traits<\\1> >', - 'experimental::basic_string_view<\\1>') + # Do not show defaulted template arguments in class templates. + add_one_template_type_printer(obj, 'unique_ptr', + { 1: 'std::default_delete<{0}>' }) + add_one_template_type_printer(obj, 'deque', { 1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'forward_list', { 1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'list', { 1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, '__cxx11::list', { 1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'vector', { 1: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'map', + { 2: 'std::less<{0}>', + 3: 'std::allocator<std::pair<{0} const, {1}>>' }) + add_one_template_type_printer(obj, 'multimap', + { 2: 'std::less<{0}>', + 3: 'std::allocator<std::pair<{0} const, {1}>>' }) + add_one_template_type_printer(obj, 'set', + { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) + add_one_template_type_printer(obj, 'multiset', + { 1: 'std::less<{0}>', 2: 'std::allocator<{0}>' }) + add_one_template_type_printer(obj, 'unordered_map', + { 2: 'std::hash<{0}>', + 3: 'std::equal_to<{0}>', + 4: 'std::allocator<std::pair<{0} const, {1}>>'}) + add_one_template_type_printer(obj, 'unordered_multimap', + { 2: 'std::hash<{0}>', + 3: 'std::equal_to<{0}>', + 4: 'std::allocator<std::pair<{0} const, {1}>>'}) + add_one_template_type_printer(obj, 'unordered_set', + { 1: 'std::hash<{0}>', + 2: 'std::equal_to<{0}>', + 3: 'std::allocator<{0}>'}) + add_one_template_type_printer(obj, 'unordered_multiset', + { 1: 'std::hash<{0}>', + 2: 'std::equal_to<{0}>', + 3: 'std::allocator<{0}>'}) def register_libstdcxx_printers (obj): "Register libstdc++ pretty-printers with objfile Obj." diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/80276.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/80276.cc new file mode 100644 index 00000000000..343b344c32e --- /dev/null +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/80276.cc @@ -0,0 +1,58 @@ +// { dg-do run { target c++11 } } +// { dg-options "-g -O0" } +// { dg-skip-if "" { *-*-* } { "-D_GLIBCXX_PROFILE" } } + +// Copyright (C) 2018 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include <iostream> +#include <list> +#include <memory> +#include <set> +#include <string> +#include <vector> + +template<class T> +void +placeholder(const T *s) +{ + std::cout << (void *) s; +} + +int +main() +{ + using namespace std; + unique_ptr<vector<unique_ptr<vector<int>*>>> p1; + unique_ptr<vector<unique_ptr<set<int>*>>[]> p2; + unique_ptr<set<unique_ptr<vector<int>*>>[10]> p3; + unique_ptr<vector<unique_ptr<list<std::string>[]>>[99]> p4; + // { dg-final { whatis-test p1 "std::unique_ptr<std::vector<std::unique_ptr<std::vector<int>*>>>" } } + // { dg-final { whatis-test p2 "std::unique_ptr<std::vector<std::unique_ptr<std::set<int>*>>\[\]>" } } + // { dg-final { whatis-test p3 "std::unique_ptr<std::set<std::unique_ptr<std::vector<int>*>>\[10\]>" } } + // { dg-final { whatis-test p4 "std::unique_ptr<std::vector<std::unique_ptr<std::list<std::string>\[\]>>\[99\]>" } } + + placeholder(&p1); // Mark SPOT + placeholder(&p2); + placeholder(&p3); + placeholder(&p4); + + std::cout << "\n"; + return 0; +} + +// { dg-final { gdb-test SPOT } } diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc index bfee2ce5aba..90f3994314b 100644 --- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis.cc @@ -19,7 +19,8 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -// Type printers only recognize the old std::string for now. +// GDB can't find global variables using the abi_tag attribute. +// https://sourceware.org/bugzilla/show_bug.cgi?id=19436 #define _GLIBCXX_USE_CXX11_ABI 0 #include <string> @@ -49,8 +50,6 @@ struct holder T *f; }; -typedef std::basic_string<unsigned char> ustring; - // This test is written in a somewhat funny way. // Each type under test is used twice: first, to form a pointer type, // and second, as a template parameter. This is done to work around @@ -165,14 +164,6 @@ std::knuth_b *knuth_b_ptr; holder<std::knuth_b> knuth_b_holder; // { dg-final { whatis-test knuth_b_holder "holder<std::knuth_b>" } } -ustring *ustring_ptr; -holder<ustring> ustring_holder; -// { dg-final { whatis-test ustring_holder "holder<std::basic_string<unsigned char> >" } } - -std::basic_string<signed char> *sstring_ptr; -holder< std::basic_string<signed char> > sstring_holder; -// { dg-final { whatis-test sstring_holder "holder<std::basic_string<signed char> >" } } - std::vector<std::deque<std::unique_ptr<char>>> *seq1_ptr; holder< std::vector<std::deque<std::unique_ptr<char>>> > seq1_holder; // { dg-final { whatis-test seq1_holder "holder<std::vector<std::deque<std::unique_ptr<char>>> >" } } @@ -271,10 +262,6 @@ main() placeholder(&ranlux48_holder); placeholder(&knuth_b_ptr); placeholder(&knuth_b_holder); - placeholder(&ustring_ptr); - placeholder(&ustring_holder); - placeholder(&sstring_ptr); - placeholder(&sstring_holder); placeholder(&seq1_ptr); placeholder(&seq1_holder); placeholder(&seq2_ptr); diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis2.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis2.cc new file mode 100644 index 00000000000..c63615f8c42 --- /dev/null +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/whatis2.cc @@ -0,0 +1,278 @@ +// { dg-do run { target c++11 } } +// { dg-options "-g -O0" } +// { dg-skip-if "" { *-*-* } { "-D_GLIBCXX_PROFILE" } } + +// Copyright (C) 2011-2018 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include <string> +#include <iostream> +#include <regex> +#include <memory> +#include <deque> +#include <forward_list> +#include <list> +#include <vector> +#include <map> +#include <set> +#include <unordered_map> +#include <unordered_set> +#include <random> + +template<class T> +void +placeholder(const T *s) +{ + std::cout << (void *) s; +} + +template<class T> +struct holder +{ + T *f; +}; + +// This test is written in a somewhat funny way. +// Each type under test is used twice: first, to form a pointer type, +// and second, as a template parameter. This is done to work around +// apparent GCC oddities. The pointer type is needed to ensure that +// the typedef in question ends up in the debuginfo; while the +// template type is used to ensure that a typedef-less variant is +// presented to gdb. + +int +main() +{ + std::string *string_ptr; + holder<std::string> string_holder; +// { dg-final { whatis-test string_holder "holder<std::string>" } } + std::ios *ios_ptr; + holder<std::ios> ios_holder; +// { dg-final { whatis-test ios_holder "holder<std::ios>" } } + std::streambuf *streambuf_ptr; + holder<std::streambuf> streambuf_holder; +// { dg-final { whatis-test streambuf_holder "holder<std::streambuf>" } } + std::istream *istream_ptr; + holder<std::istream> istream_holder; +// { dg-final { whatis-test istream_holder "holder<std::istream>" } } + std::ostream *ostream_ptr; + holder<std::ostream> ostream_holder; +// { dg-final { whatis-test ostream_holder "holder<std::ostream>" } } + std::iostream *iostream_ptr; + holder<std::iostream> iostream_holder; +// { dg-final { whatis-test iostream_holder "holder<std::iostream>" } } + std::stringbuf *stringbuf_ptr; + holder<std::stringbuf> stringbuf_holder; +// { dg-final { whatis-test stringbuf_holder "holder<std::stringbuf>" } } + std::istringstream *istringstream_ptr; + holder<std::istringstream> istringstream_holder; +// { dg-final { whatis-test istringstream_holder "holder<std::istringstream>" } } + std::ostringstream *ostringstream_ptr; + holder<std::ostringstream> ostringstream_holder; +// { dg-final { whatis-test ostringstream_holder "holder<std::ostringstream>" } } + std::stringstream *stringstream_ptr; + holder<std::stringstream> stringstream_holder; +// { dg-final { whatis-test stringstream_holder "holder<std::stringstream>" } } + std::filebuf *filebuf_ptr; + holder<std::filebuf> filebuf_holder; +// { dg-final { whatis-test filebuf_holder "holder<std::filebuf>" } } + std::ifstream *ifstream_ptr; + holder<std::ifstream> ifstream_holder; +// { dg-final { whatis-test ifstream_holder "holder<std::ifstream>" } } + std::ofstream *ofstream_ptr; + holder<std::ofstream> ofstream_holder; +// { dg-final { whatis-test ofstream_holder "holder<std::ofstream>" } } + std::fstream *fstream_ptr; + holder<std::fstream> fstream_holder; +// { dg-final { whatis-test fstream_holder "holder<std::fstream>" } } + std::streampos *streampos_ptr; + holder<std::streampos> streampos_holder; +// { dg-final { whatis-test streampos_holder "holder<std::streampos>" } } + std::regex *regex_ptr; + holder<std::regex> regex_holder; +// { dg-final { whatis-test regex_holder "holder<std::regex>" } } +std::csub_match *csub_match_ptr; +holder<std::csub_match> csub_match_holder; +// { dg-final { whatis-test csub_match_holder "holder<std::csub_match>" } } + std::ssub_match *ssub_match_ptr; + holder<std::ssub_match> ssub_match_holder; +// { dg-final { whatis-test ssub_match_holder "holder<std::ssub_match>" } } + std::cmatch *cmatch_ptr; + holder<std::cmatch> cmatch_holder; +// { dg-final { whatis-test cmatch_holder "holder<std::cmatch>" } } + std::smatch *smatch_ptr; + holder<std::smatch> smatch_holder; +// { dg-final { whatis-test smatch_holder "holder<std::smatch>" } } + std::cregex_iterator *cregex_iterator_ptr; + holder<std::cregex_iterator> cregex_iterator_holder; +// { dg-final { whatis-test cregex_iterator_holder "holder<std::cregex_iterator>" } } + std::sregex_iterator *sregex_iterator_ptr; + holder<std::sregex_iterator> sregex_iterator_holder; +// { dg-final { whatis-test sregex_iterator_holder "holder<std::sregex_iterator>" } } + std::cregex_token_iterator *cregex_token_iterator_ptr; + holder<std::cregex_token_iterator> cregex_token_iterator_holder; +// { dg-final { whatis-test cregex_token_iterator_holder "holder<std::cregex_token_iterator>" } } + std::sregex_token_iterator *sregex_token_iterator_ptr; + holder<std::sregex_token_iterator> sregex_token_iterator_holder; +// { dg-final { whatis-test sregex_token_iterator_holder "holder<std::sregex_token_iterator>" } } + std::u16string *u16string_ptr; + holder<std::u16string> u16string_holder; +// { dg-final { whatis-test u16string_holder "holder<std::u16string>" } } + std::u32string *u32string_ptr; + holder<std::u32string> u32string_holder; +// { dg-final { whatis-test u32string_holder "holder<std::u32string>" } } + std::minstd_rand0 *minstd_rand0_ptr; + holder<std::minstd_rand0> minstd_rand0_holder; +// { dg-final { whatis-test minstd_rand0_holder "holder<std::minstd_rand0>" } } + std::minstd_rand *minstd_rand_ptr; + holder<std::minstd_rand> minstd_rand_holder; +// { dg-final { whatis-test minstd_rand_holder "holder<std::minstd_rand>" } } + std::mt19937 *mt19937_ptr; + holder<std::mt19937> mt19937_holder; +// { dg-final { whatis-test mt19937_holder "holder<std::mt19937>" } } + std::mt19937_64 *mt19937_64_ptr; + holder<std::mt19937_64> mt19937_64_holder; +// { dg-final { whatis-test mt19937_64_holder "holder<std::mt19937_64>" } } + std::ranlux24_base *ranlux24_base_ptr; + holder<std::ranlux24_base> ranlux24_base_holder; +// { dg-final { whatis-test ranlux24_base_holder "holder<std::ranlux24_base>" } } + std::ranlux48_base *ranlux48_base_ptr; + holder<std::ranlux48_base> ranlux48_base_holder; +// { dg-final { whatis-test ranlux48_base_holder "holder<std::ranlux48_base>" } } + std::ranlux24 *ranlux24_ptr; + holder<std::ranlux24> ranlux24_holder; +// { dg-final { whatis-test ranlux24_holder "holder<std::ranlux24>" } } + std::ranlux48 *ranlux48_ptr; + holder<std::ranlux48> ranlux48_holder; +// { dg-final { whatis-test ranlux48_holder "holder<std::ranlux48>" } } + std::knuth_b *knuth_b_ptr; + holder<std::knuth_b> knuth_b_holder; +// { dg-final { whatis-test knuth_b_holder "holder<std::knuth_b>" } } + + std::vector<std::deque<std::unique_ptr<char>>> *seq1_ptr; + holder< std::vector<std::deque<std::unique_ptr<char>>> > seq1_holder; +// { dg-final { whatis-test seq1_holder "holder<std::vector<std::deque<std::unique_ptr<char>>> >" } } + + std::list<std::forward_list<std::unique_ptr<char>>> *seq2_ptr; + holder< std::list<std::forward_list<std::unique_ptr<char>>> > seq2_holder; +// { dg-final { whatis-test seq2_holder "holder<std::list<std::forward_list<std::unique_ptr<char>>> >" } } + + std::map<int, std::set<int>> *assoc1_ptr; + holder< std::map<int, std::set<int>> > assoc1_holder; +// { dg-final { whatis-test assoc1_holder "holder<std::map<int, std::set<int>> >" } } + + std::multimap<int, std::multiset<int>> *assoc2_ptr; + holder< std::multimap<int, std::multiset<int>> > assoc2_holder; +// { dg-final { whatis-test assoc2_holder "holder<std::multimap<int, std::multiset<int>> >" } } + + std::unordered_map<int, std::unordered_set<int>> *unord1_ptr; + holder< std::unordered_map<int, std::unordered_set<int>> > unord1_holder; +// { dg-final { whatis-test unord1_holder "holder<std::unordered_map<int, std::unordered_set<int>> >" } } + + std::unordered_multimap<int, std::unordered_multiset<int>> *unord2_ptr; + holder< std::unordered_multimap<int, std::unordered_multiset<int>> > unord2_holder; +// { dg-final { whatis-test unord2_holder "holder<std::unordered_multimap<int, std::unordered_multiset<int>> >" } } + + + placeholder(&ios_ptr); // Mark SPOT + placeholder(&ios_holder); + placeholder(&string_ptr); + placeholder(&string_holder); + placeholder(&streambuf_ptr); + placeholder(&streambuf_holder); + placeholder(&istream_ptr); + placeholder(&istream_holder); + placeholder(&ostream_ptr); + placeholder(&ostream_holder); + placeholder(&iostream_ptr); + placeholder(&iostream_holder); + placeholder(&stringbuf_ptr); + placeholder(&stringbuf_holder); + placeholder(&istringstream_ptr); + placeholder(&istringstream_holder); + placeholder(&ostringstream_ptr); + placeholder(&ostringstream_holder); + placeholder(&stringstream_ptr); + placeholder(&stringstream_holder); + placeholder(&filebuf_ptr); + placeholder(&filebuf_holder); + placeholder(&ifstream_ptr); + placeholder(&ifstream_holder); + placeholder(&ofstream_ptr); + placeholder(&ofstream_holder); + placeholder(&fstream_ptr); + placeholder(&fstream_holder); + placeholder(&streampos_ptr); + placeholder(&streampos_holder); + placeholder(®ex_ptr); + placeholder(®ex_holder); + placeholder(&csub_match_ptr); + placeholder(&csub_match_holder); + placeholder(&ssub_match_ptr); + placeholder(&ssub_match_holder); + placeholder(&cmatch_ptr); + placeholder(&cmatch_holder); + placeholder(&smatch_ptr); + placeholder(&smatch_holder); + placeholder(&cregex_iterator_ptr); + placeholder(&cregex_iterator_holder); + placeholder(&sregex_iterator_ptr); + placeholder(&sregex_iterator_holder); + placeholder(&cregex_token_iterator_ptr); + placeholder(&cregex_token_iterator_holder); + placeholder(&sregex_token_iterator_ptr); + placeholder(&sregex_token_iterator_holder); + placeholder(&u16string_ptr); + placeholder(&u16string_holder); + placeholder(&u32string_ptr); + placeholder(&u32string_holder); + placeholder(&minstd_rand0_ptr); + placeholder(&minstd_rand0_holder); + placeholder(&minstd_rand_ptr); + placeholder(&minstd_rand_holder); + placeholder(&mt19937_ptr); + placeholder(&mt19937_holder); + placeholder(&mt19937_64_ptr); + placeholder(&mt19937_64_holder); + placeholder(&ranlux24_base_ptr); + placeholder(&ranlux24_base_holder); + placeholder(&ranlux48_base_ptr); + placeholder(&ranlux48_base_holder); + placeholder(&ranlux24_ptr); + placeholder(&ranlux24_holder); + placeholder(&ranlux48_ptr); + placeholder(&ranlux48_holder); + placeholder(&knuth_b_ptr); + placeholder(&knuth_b_holder); + placeholder(&seq1_ptr); + placeholder(&seq1_holder); + placeholder(&seq2_ptr); + placeholder(&seq2_holder); + placeholder(&assoc1_ptr); + placeholder(&assoc1_holder); + placeholder(&assoc2_ptr); + placeholder(&assoc2_holder); + placeholder(&unord1_ptr); + placeholder(&unord1_holder); + placeholder(&unord2_ptr); + placeholder(&unord2_holder); + + std::cout << "\n"; + return 0; +} + +// { dg-final { gdb-test SPOT } }
On 13/01/18 00:29 +0000, Jonathan Wakely wrote: >Secondly, std::experimental::optional no longer omits the >fundamentals_v1 inline namespace ... but then it didn't work properly >anyway because it displayed as "std::experimental::optional<\1>". So >arguably it's better to have no type printer than one that displays >the wrong type! I've also removed the type printer for >std::experimental::any was bogus, because it tried to match a >template, and any isn't a template! Here's a patch that just fixes the optional<\1> bug, which I'll commit to the branches. diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index a67b27ab736..8f1023949bb 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -1277,7 +1277,7 @@ class TemplateTypePrinter(object): Recognizes type names that match a regular expression. Replaces them with a formatted string which can use replacement field {N} to refer to the \N subgroup of the regex match. - Type printers are recusively applied to the subgroups. + Type printers are recursively applied to the subgroups. This allows recognizing e.g. "std::vector<(.*), std::allocator<\\1> >" and replacing it with "std::vector<{1}>", omitting the template argument @@ -1473,15 +1473,15 @@ def register_type_printers(obj): 'unordered_multiset<{1}>') # strip the "fundamentals_v1" inline namespace from these types - add_one_template_type_printer(obj, 'any<T>', - 'experimental::fundamentals_v\d::any<(.*)>', - 'experimental::any<\\1>') + add_one_template_type_printer(obj, 'any', + 'experimental::fundamentals_v\d::any', + 'experimental::any') add_one_template_type_printer(obj, 'optional<T>', 'experimental::fundamentals_v\d::optional<(.*)>', - 'experimental::optional<\\1>') + 'experimental::optional<{1}>') add_one_template_type_printer(obj, 'basic_string_view<C>', 'experimental::fundamentals_v\d::basic_string_view<(.*), std::char_traits<\\1> >', - 'experimental::basic_string_view<\\1>') + 'experimental::basic_string_view<{1}>') def register_libstdcxx_printers (obj): "Register libstdc++ pretty-printers with objfile Obj."
diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index e9f7359d63f..d6de0969e1c 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -150,7 +150,7 @@ class SharedPointerPrinter: state = 'expired, weak count %d' % weakcount else: state = 'use count %d, weak count %d' % (usecount, weakcount - 1) - return '%s<%s> (%s)' % (self.typename, str(self.pointer.type.target().strip_typedefs()), state) + return '%s<%s> (%s)' % (self.typename, str(self.val.type.template_argument(0)), state) class UniquePointerPrinter: "Print a unique_ptr" @@ -169,7 +169,7 @@ class UniquePointerPrinter: return SmartPtrIterator(self.pointer) def to_string (self): - return ('std::unique_ptr<%s>' % (str(self.pointer.type.target()))) + return ('std::unique_ptr<%s>' % (str(self.val.type.template_argument(0)))) def get_value_from_aligned_membuf(buf, valtype): """Returns the value held in a __gnu_cxx::__aligned_membuf.""" @@ -1328,9 +1328,13 @@ class TemplateTypePrinter(object): for i, sub in enumerate(subs): if ('{%d}' % (i+1)) in self.subst: # apply recognizers to subgroup + try: + subtype = gdb.lookup_type(sub) + except gdb.error: + continue rep = gdb.types.apply_type_recognizers( gdb.types.get_type_recognizers(), - gdb.lookup_type(sub)) + subtype) if rep: subs[i] = rep subs = [None] + subs diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc index 3af564f2bf7..0ecc3771351 100644 --- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx11.cc @@ -131,6 +131,12 @@ main() std::unique_ptr<datum> &ruptr = uptr; // { dg-final { regexp-test ruptr {std::unique_ptr.datum. = {get\(\) = 0x.*}} } } + using data = datum[]; + std::unique_ptr<data> arrptr (new datum[2]); +// { dg-final { regexp-test arrptr {std::unique_ptr.datum \[\]. = {get\(\) = 0x.*}} } } + std::unique_ptr<data>& rarrptr = arrptr; +// { dg-final { regexp-test rarrptr {std::unique_ptr.datum \[\]. = {get\(\) = 0x.*}} } } + ExTuple tpl(6,7); // { dg-final { note-test tpl {std::tuple containing = {[1] = 6, [2] = 7}} } } ExTuple &rtpl = tpl; @@ -144,6 +150,7 @@ main() use(eums); use(uoms); use(uptr->s); + use(arrptr[0].s); std::cout << "\n"; return 0; diff --git a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc index c11bb33086b..0c7cb4c9bb6 100644 --- a/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc +++ b/libstdc++-v3/testsuite/libstdc++-prettyprinters/cxx17.cc @@ -29,6 +29,7 @@ #include <string> #include <map> #include <unordered_set> +#include <memory> #include <iostream> using std::any; @@ -37,6 +38,8 @@ using std::variant; using std::string_view; using std::map; using std::unordered_set; +using std::shared_ptr; +using std::weak_ptr; int main() @@ -100,6 +103,18 @@ main() unordered_set<int>::node_type n3 = s.extract(3); // { dg-final { note-test n1 {node handle for unordered set with element = {3}}}} + shared_ptr<int[]> p(new int[1]); + weak_ptr wp = p; + weak_ptr wp2 = p; +// { dg-final { regexp-test p {std::shared_ptr.int \[\]. \(use count 1, weak count 2\) = {get\(\) = 0x.*}} } } +// { dg-final { regexp-test wp {std::weak_ptr.int \[\]. \(use count 1, weak count 2\) = {get\(\) = 0x.*}} } } + + shared_ptr<int[2]> q(new int[2]); + shared_ptr q2 = q; + weak_ptr wq = q; +// { dg-final { regexp-test q {std::shared_ptr.int \[2\]. \(use count 2, weak count 1\) = {get\(\) = 0x.*}} } } +// { dg-final { regexp-test wq {std::weak_ptr.int \[2\]. \(use count 2, weak count 1\) = {get\(\) = 0x.*}} } } + std::cout << "\n"; return 0; // Mark SPOT }