| /* Convert types from GDB to GCC | |
| Copyright (C) 2014-2023 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| This program 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 of the License, or | |
| (at your option) any later version. | |
| This program 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 program. If not, see <http://www.gnu.org/licenses/>. */ | |
| #include "defs.h" | |
| #include "gdbsupport/preprocessor.h" | |
| #include "gdbtypes.h" | |
| #include "compile-internal.h" | |
| #include "compile-cplus.h" | |
| #include "gdbsupport/gdb_assert.h" | |
| #include "symtab.h" | |
| #include "source.h" | |
| #include "cp-support.h" | |
| #include "cp-abi.h" | |
| #include "objfiles.h" | |
| #include "block.h" | |
| #include "gdbcmd.h" | |
| #include "c-lang.h" | |
| #include "compile-c.h" | |
| #include <algorithm> | |
| /* Default compile flags for C++. */ | |
| const char *compile_cplus_instance::m_default_cflags = "-std=gnu++11"; | |
| /* Flag to enable internal debugging. */ | |
| static bool debug_compile_cplus_types = false; | |
| /* Flag to enable internal scope switching debugging. */ | |
| static bool debug_compile_cplus_scopes = false; | |
| /* Forward declarations. */ | |
| static gcc_type compile_cplus_convert_func (compile_cplus_instance *instance, | |
| struct type *type, | |
| bool strip_artificial); | |
| /* See description in compile-cplus.h. */ | |
| gdb::unique_xmalloc_ptr<char> | |
| compile_cplus_instance::decl_name (const char *natural) | |
| { | |
| if (natural == nullptr) | |
| return nullptr; | |
| gdb::unique_xmalloc_ptr<char> name = cp_func_name (natural); | |
| if (name != nullptr) | |
| return name; | |
| return make_unique_xstrdup (natural); | |
| } | |
| /* Get the access flag for the NUM'th field of TYPE. */ | |
| static enum gcc_cp_symbol_kind | |
| get_field_access_flag (const struct type *type, int num) | |
| { | |
| if (TYPE_FIELD_PROTECTED (type, num)) | |
| return GCC_CP_ACCESS_PROTECTED; | |
| else if (TYPE_FIELD_PRIVATE (type, num)) | |
| return GCC_CP_ACCESS_PRIVATE; | |
| /* GDB assumes everything else is public. */ | |
| return GCC_CP_ACCESS_PUBLIC; | |
| } | |
| /* Get the access flag for the NUM'th method of TYPE's FNI'th | |
| fieldlist. */ | |
| enum gcc_cp_symbol_kind | |
| get_method_access_flag (const struct type *type, int fni, int num) | |
| { | |
| gdb_assert (type->code () == TYPE_CODE_STRUCT); | |
| /* If this type was not declared a class, everything is public. */ | |
| if (!type->is_declared_class ()) | |
| return GCC_CP_ACCESS_PUBLIC; | |
| /* Otherwise, read accessibility from the fn_field. */ | |
| const struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, fni); | |
| if (TYPE_FN_FIELD_PROTECTED (methods, num)) | |
| return GCC_CP_ACCESS_PROTECTED; | |
| else if (TYPE_FN_FIELD_PRIVATE (methods, num)) | |
| return GCC_CP_ACCESS_PRIVATE; | |
| else | |
| return GCC_CP_ACCESS_PUBLIC; | |
| } | |
| /* A useful debugging function to output the scope SCOPE to stdout. */ | |
| static void __attribute__ ((used)) | |
| debug_print_scope (const compile_scope &scope) | |
| { | |
| for (const auto &comp: scope) | |
| { | |
| const char *symbol = (comp.bsymbol.symbol != nullptr | |
| ? comp.bsymbol.symbol->natural_name () | |
| : "<none>"); | |
| printf_unfiltered ("\tname = %s, symbol = %s\n", comp.name.c_str (), | |
| symbol); | |
| } | |
| } | |
| /* See description in compile-cplus.h. */ | |
| compile_scope | |
| type_name_to_scope (const char *type_name, const struct block *block) | |
| { | |
| compile_scope scope; | |
| if (type_name == nullptr) | |
| { | |
| /* An anonymous type. We cannot really do much here. We simply cannot | |
| look up anonymous types easily/at all. */ | |
| return scope; | |
| } | |
| const char *p = type_name; | |
| std::string lookup_name; | |
| while (*p != '\0') | |
| { | |
| /* Create a string token of the first component of TYPE_NAME. */ | |
| int len = cp_find_first_component (p); | |
| std::string s (p, len); | |
| /* Advance past the last token. */ | |
| p += len; | |
| /* Look up the symbol and decide when to stop. */ | |
| if (!lookup_name.empty ()) | |
| lookup_name += "::"; | |
| lookup_name += s; | |
| /* Look up the resulting name. */ | |
| struct block_symbol bsymbol | |
| = lookup_symbol (lookup_name.c_str (), block, VAR_DOMAIN, nullptr); | |
| if (bsymbol.symbol != nullptr) | |
| { | |
| scope_component comp = {s, bsymbol}; | |
| scope.push_back (comp); | |
| if (bsymbol.symbol->type ()->code () != TYPE_CODE_NAMESPACE) | |
| { | |
| /* We're done. */ | |
| break; | |
| } | |
| } | |
| if (*p == ':') | |
| { | |
| ++p; | |
| if (*p == ':') | |
| ++p; | |
| else | |
| { | |
| /* This shouldn't happen since we are not attempting to | |
| loop over user input. This name is generated by GDB | |
| from debug info. */ | |
| internal_error (_("malformed TYPE_NAME during parsing")); | |
| } | |
| } | |
| } | |
| return scope; | |
| } | |
| /* Compare two scope_components for equality. These are equal if the names | |
| of the two components' are the same. */ | |
| bool | |
| operator== (const scope_component &lhs, const scope_component &rhs) | |
| { | |
| return lhs.name == rhs.name; | |
| } | |
| /* Compare two scope_components for inequality. These are not equal if | |
| the two components' names are not equal. */ | |
| bool | |
| operator!= (const scope_component &lhs, const scope_component &rhs) | |
| { | |
| return lhs.name != rhs.name; | |
| } | |
| /* Compare two compile_scopes for equality. These are equal if they are both | |
| contain the same number of components and each component is equal. */ | |
| bool | |
| operator== (const compile_scope &lhs, const compile_scope &rhs) | |
| { | |
| if (lhs.size () != rhs.size ()) | |
| return false; | |
| for (int i = 0; i < lhs.size (); ++i) | |
| { | |
| if (lhs[i] != rhs[i]) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /* Compare two compile_scopes for inequality. These are inequal if they | |
| contain unequal number of elements or if any of the components are not | |
| the same. */ | |
| bool | |
| operator!= (const compile_scope &lhs, const compile_scope &rhs) | |
| { | |
| if (lhs.size () != rhs.size ()) | |
| return true; | |
| for (int i = 0; i < lhs.size (); ++i) | |
| { | |
| if (lhs[i] != rhs[i]) | |
| return true; | |
| } | |
| return false; | |
| } | |
| /* See description in compile-cplus.h. */ | |
| void | |
| compile_cplus_instance::enter_scope (compile_scope &&new_scope) | |
| { | |
| bool must_push = m_scopes.empty () || m_scopes.back () != new_scope; | |
| new_scope.m_pushed = must_push; | |
| /* Save the new scope. */ | |
| m_scopes.push_back (std::move (new_scope)); | |
| if (must_push) | |
| { | |
| if (debug_compile_cplus_scopes) | |
| { | |
| gdb_printf (gdb_stdlog, "entering new scope %s\n", | |
| host_address_to_string (&m_scopes.back ())); | |
| } | |
| /* Push the global namespace. */ | |
| plugin ().push_namespace (""); | |
| /* Push all other namespaces. Note that we do not push the last | |
| scope_component -- that's the actual type we are converting. */ | |
| std::for_each | |
| (m_scopes.back ().begin (), m_scopes.back ().end () - 1, | |
| [this] (const scope_component &comp) | |
| { | |
| gdb_assert (comp.bsymbol.symbol->type ()->code () | |
| == TYPE_CODE_NAMESPACE); | |
| const char *ns = (comp.name == CP_ANONYMOUS_NAMESPACE_STR ? nullptr | |
| : comp.name.c_str ()); | |
| this->plugin ().push_namespace (ns); | |
| }); | |
| } | |
| else | |
| { | |
| if (debug_compile_cplus_scopes) | |
| { | |
| gdb_printf (gdb_stdlog, "staying in current scope -- " | |
| "scopes are identical\n"); | |
| } | |
| } | |
| } | |
| /* See description in compile-cplus.h. */ | |
| void | |
| compile_cplus_instance::leave_scope () | |
| { | |
| /* Get the current scope and remove it from the internal list of | |
| scopes. */ | |
| compile_scope current = m_scopes.back (); | |
| m_scopes.pop_back (); | |
| if (current.m_pushed) | |
| { | |
| if (debug_compile_cplus_scopes) | |
| { | |
| gdb_printf (gdb_stdlog, "leaving scope %s\n", | |
| host_address_to_string (¤t)); | |
| } | |
| /* Pop namespaces. */ | |
| std::for_each | |
| (current.begin (),current.end () - 1, | |
| [this] (const scope_component &comp) { | |
| gdb_assert (comp.bsymbol.symbol->type ()->code () | |
| == TYPE_CODE_NAMESPACE); | |
| this->plugin ().pop_binding_level (comp.name.c_str ()); | |
| }); | |
| /* Pop global namespace. */ | |
| plugin ().pop_binding_level (""); | |
| } | |
| else | |
| { | |
| if (debug_compile_cplus_scopes) | |
| gdb_printf (gdb_stdlog, | |
| "identical scopes -- not leaving scope\n"); | |
| } | |
| } | |
| /* See description in compile-cplus.h. */ | |
| compile_scope | |
| compile_cplus_instance::new_scope (const char *type_name, struct type *type) | |
| { | |
| /* Break the type name into components. If TYPE was defined in some | |
| superclass, we do not process TYPE but process the enclosing type | |
| instead. */ | |
| compile_scope scope = type_name_to_scope (type_name, block ()); | |
| if (!scope.empty ()) | |
| { | |
| /* Get the name of the last component, which should be the | |
| unqualified name of the type to process. */ | |
| scope_component &comp = scope.back (); | |
| if (!types_equal (type, comp.bsymbol.symbol->type ()) | |
| && (m_scopes.empty () | |
| || (m_scopes.back ().back ().bsymbol.symbol | |
| != comp.bsymbol.symbol))) | |
| { | |
| /* The type is defined inside another class(es). Convert that | |
| type instead of defining this type. */ | |
| convert_type (comp.bsymbol.symbol->type ()); | |
| /* If the original type (passed in to us) is defined in a nested | |
| class, the previous call will give us that type's gcc_type. | |
| Upper layers are expecting to get the original type's | |
| gcc_type! */ | |
| get_cached_type (type, &scope.m_nested_type); | |
| return scope; | |
| } | |
| } | |
| else | |
| { | |
| if (type->name () == nullptr) | |
| { | |
| /* Anonymous type */ | |
| /* We don't have a qualified name for this to look up, but | |
| we need a scope. We have to assume, then, that it is the same | |
| as the current scope, if any. */ | |
| if (!m_scopes.empty ()) | |
| { | |
| scope = m_scopes.back (); | |
| scope.m_pushed = false; | |
| } | |
| else | |
| scope.push_back (scope_component ()); | |
| } | |
| else | |
| { | |
| scope_component comp | |
| = { | |
| decl_name (type->name ()).get (), | |
| lookup_symbol (type->name (), block (), VAR_DOMAIN, nullptr) | |
| }; | |
| scope.push_back (comp); | |
| } | |
| } | |
| /* There must be at least one component in the compile_scope. */ | |
| gdb_assert (scope.size () > 0); | |
| return scope; | |
| } | |
| /* See description in compile-cplus.h. */ | |
| gcc_type | |
| compile_cplus_instance::convert_reference_base | |
| (gcc_type base, enum gcc_cp_ref_qualifiers rquals) | |
| { | |
| return this->plugin ().build_reference_type (base, rquals); | |
| } | |
| /* Convert a reference type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_reference (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| gcc_type target = instance->convert_type (type->target_type ()); | |
| enum gcc_cp_ref_qualifiers quals = GCC_CP_REF_QUAL_NONE; | |
| switch (type->code ()) | |
| { | |
| case TYPE_CODE_REF: | |
| quals = GCC_CP_REF_QUAL_LVALUE; | |
| break; | |
| case TYPE_CODE_RVALUE_REF: | |
| quals = GCC_CP_REF_QUAL_RVALUE; | |
| break; | |
| default: | |
| gdb_assert_not_reached ("unexpected type code for reference type"); | |
| } | |
| return instance->convert_reference_base (target, quals); | |
| } | |
| /* See description in compile-cplus.h. */ | |
| gcc_type | |
| compile_cplus_instance::convert_pointer_base(gcc_type target) | |
| { | |
| return plugin ().build_pointer_type (target); | |
| } | |
| /* Convert a pointer type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_pointer (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| gcc_type target = instance->convert_type (type->target_type ()); | |
| return instance->convert_pointer_base (target); | |
| } | |
| /* Convert an array type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_array (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| struct type *range = type->index_type (); | |
| gcc_type element_type = instance->convert_type (type->target_type ()); | |
| if (!range->bounds ()->low.is_constant ()) | |
| { | |
| const char *s = _("array type with non-constant" | |
| " lower bound is not supported"); | |
| return instance->plugin ().error (s); | |
| } | |
| if (range->bounds ()->low.const_val () != 0) | |
| { | |
| const char *s = _("cannot convert array type with " | |
| "non-zero lower bound to C"); | |
| return instance->plugin ().error (s); | |
| } | |
| if (range->bounds ()->high.kind () == PROP_LOCEXPR | |
| || range->bounds ()->high.kind () == PROP_LOCLIST) | |
| { | |
| if (type->is_vector ()) | |
| { | |
| const char *s = _("variably-sized vector type is not supported"); | |
| return instance->plugin ().error (s); | |
| } | |
| std::string upper_bound | |
| = c_get_range_decl_name (&range->bounds ()->high); | |
| return instance->plugin ().build_vla_array_type (element_type, | |
| upper_bound.c_str ()); | |
| } | |
| else | |
| { | |
| LONGEST low_bound, high_bound, count; | |
| if (!get_array_bounds (type, &low_bound, &high_bound)) | |
| count = -1; | |
| else | |
| { | |
| gdb_assert (low_bound == 0); /* Ensured above. */ | |
| count = high_bound + 1; | |
| } | |
| if (type->is_vector ()) | |
| return instance->plugin ().build_vector_type (element_type, count); | |
| return instance->plugin ().build_array_type (element_type, count); | |
| } | |
| } | |
| /* Convert a typedef of TYPE. If not GCC_CP_ACCESS_NONE, NESTED_ACCESS | |
| will define the accessibility of the typedef definition in its | |
| containing class. */ | |
| static gcc_type | |
| compile_cplus_convert_typedef (compile_cplus_instance *instance, | |
| struct type *type, | |
| enum gcc_cp_symbol_kind nested_access) | |
| { | |
| compile_scope scope = instance->new_scope (type->name (), type); | |
| if (scope.nested_type () != GCC_TYPE_NONE) | |
| return scope.nested_type (); | |
| gdb::unique_xmalloc_ptr<char> name | |
| = compile_cplus_instance::decl_name (type->name ()); | |
| /* Make sure the scope for this type has been pushed. */ | |
| instance->enter_scope (std::move (scope)); | |
| /* Convert the typedef's real type. */ | |
| gcc_type typedef_type = instance->convert_type (check_typedef (type)); | |
| instance->plugin ().build_decl ("typedef", name.get (), | |
| GCC_CP_SYMBOL_TYPEDEF | nested_access, | |
| typedef_type, 0, 0, nullptr, 0); | |
| /* Completed this scope. */ | |
| instance->leave_scope (); | |
| return typedef_type; | |
| } | |
| /* Convert types defined in TYPE. */ | |
| static void | |
| compile_cplus_convert_type_defns (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| int i; | |
| enum gcc_cp_symbol_kind accessibility; | |
| /* Convert typedefs. */ | |
| for (i = 0; i < TYPE_TYPEDEF_FIELD_COUNT (type); ++i) | |
| { | |
| if (TYPE_TYPEDEF_FIELD_PROTECTED (type, i)) | |
| accessibility = GCC_CP_ACCESS_PROTECTED; | |
| else if (TYPE_TYPEDEF_FIELD_PRIVATE (type, i)) | |
| accessibility = GCC_CP_ACCESS_PRIVATE; | |
| else | |
| accessibility = GCC_CP_ACCESS_PUBLIC; | |
| instance->convert_type (TYPE_TYPEDEF_FIELD_TYPE (type, i), accessibility); | |
| } | |
| /* Convert nested types. */ | |
| for (i = 0; i < TYPE_NESTED_TYPES_COUNT (type); ++i) | |
| { | |
| if (TYPE_NESTED_TYPES_FIELD_PROTECTED (type, i)) | |
| accessibility = GCC_CP_ACCESS_PROTECTED; | |
| else if (TYPE_NESTED_TYPES_FIELD_PRIVATE (type, i)) | |
| accessibility = GCC_CP_ACCESS_PRIVATE; | |
| else | |
| accessibility = GCC_CP_ACCESS_PUBLIC; | |
| instance->convert_type (TYPE_NESTED_TYPES_FIELD_TYPE (type, i), | |
| accessibility); | |
| } | |
| } | |
| /* Convert data members defined in TYPE, which should be struct/class/union | |
| with gcc_type COMP_TYPE. */ | |
| static void | |
| compile_cplus_convert_struct_or_union_members | |
| (compile_cplus_instance *instance, struct type *type, gcc_type comp_type) | |
| { | |
| for (int i = TYPE_N_BASECLASSES (type); i < type->num_fields (); ++i) | |
| { | |
| const char *field_name = type->field (i).name (); | |
| if (TYPE_FIELD_IGNORE (type, i) | |
| || TYPE_FIELD_ARTIFICIAL (type, i)) | |
| continue; | |
| /* GDB records unnamed/anonymous fields with empty string names. */ | |
| if (*field_name == '\0') | |
| field_name = nullptr; | |
| gcc_type field_type | |
| = instance->convert_type (type->field (i).type ()); | |
| if (type->field (i).is_static ()) | |
| { | |
| CORE_ADDR physaddr; | |
| switch (type->field (i).loc_kind ()) | |
| { | |
| case FIELD_LOC_KIND_PHYSADDR: | |
| { | |
| physaddr = type->field (i).loc_physaddr (); | |
| instance->plugin ().build_decl | |
| ("field physaddr", field_name, | |
| (GCC_CP_SYMBOL_VARIABLE | get_field_access_flag (type, i)), | |
| field_type, nullptr, physaddr, nullptr, 0); | |
| } | |
| break; | |
| case FIELD_LOC_KIND_PHYSNAME: | |
| { | |
| const char *physname = type->field (i).loc_physname (); | |
| struct block_symbol sym | |
| = lookup_symbol (physname, instance->block (), | |
| VAR_DOMAIN, nullptr); | |
| if (sym.symbol == nullptr) | |
| { | |
| /* We didn't actually find the symbol. There's little | |
| we can do but ignore this member. */ | |
| continue; | |
| } | |
| const char *filename = sym.symbol->symtab ()->filename; | |
| unsigned int line = sym.symbol->line (); | |
| physaddr = sym.symbol->value_address (); | |
| instance->plugin ().build_decl | |
| ("field physname", field_name, | |
| (GCC_CP_SYMBOL_VARIABLE| get_field_access_flag (type, i)), | |
| field_type, nullptr, physaddr, filename, line); | |
| } | |
| break; | |
| default: | |
| gdb_assert_not_reached | |
| ("unexpected static field location kind"); | |
| } | |
| } | |
| else | |
| { | |
| unsigned long bitsize = TYPE_FIELD_BITSIZE (type, i); | |
| enum gcc_cp_symbol_kind field_flags = GCC_CP_SYMBOL_FIELD | |
| | get_field_access_flag (type, i); | |
| if (bitsize == 0) | |
| bitsize = 8 * type->field (i).type ()->length (); | |
| instance->plugin ().build_field | |
| (field_name, field_type, field_flags, bitsize, | |
| type->field (i).loc_bitpos ()); | |
| } | |
| } | |
| } | |
| /* Convert a method type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_method (compile_cplus_instance *instance, | |
| struct type *parent_type, | |
| struct type *method_type) | |
| { | |
| /* Get the actual function type of the method, the corresponding class's | |
| type and corresponding qualifier flags. */ | |
| gcc_type func_type = compile_cplus_convert_func (instance, method_type, true); | |
| gcc_type class_type = instance->convert_type (parent_type); | |
| gcc_cp_qualifiers_flags quals = 0; | |
| if (TYPE_CONST (method_type)) | |
| quals |= GCC_CP_QUALIFIER_CONST; | |
| if (TYPE_VOLATILE (method_type)) | |
| quals |= GCC_CP_QUALIFIER_VOLATILE; | |
| if (TYPE_RESTRICT (method_type)) | |
| quals |= GCC_CP_QUALIFIER_RESTRICT; | |
| /* Not yet implemented. */ | |
| gcc_cp_ref_qualifiers_flags rquals = GCC_CP_REF_QUAL_NONE; | |
| return instance->plugin ().build_method_type | |
| (class_type, func_type, quals.raw (), rquals.raw ()); | |
| } | |
| /* Convert a member or method pointer represented by TYPE. */ | |
| static gcc_type | |
| compile_cplus_convert_memberptr (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| struct type *containing_class = TYPE_SELF_TYPE (type); | |
| if (containing_class == nullptr) | |
| return GCC_TYPE_NONE; | |
| gcc_type class_type = instance->convert_type (containing_class); | |
| gcc_type member_type | |
| = instance->convert_type (type->target_type ()); | |
| return instance->plugin ().build_pointer_to_member_type | |
| (class_type, member_type); | |
| } | |
| /* Convert all methods defined in TYPE, which should be a class/struct/union | |
| with gcc_type CLASS_TYPE. */ | |
| static void | |
| compile_cplus_convert_struct_or_union_methods (compile_cplus_instance *instance, | |
| struct type *type, | |
| gcc_type class_type) | |
| { | |
| for (int i = 0; i < TYPE_NFN_FIELDS (type); ++i) | |
| { | |
| struct fn_field *methods = TYPE_FN_FIELDLIST1 (type, i); | |
| gdb::unique_xmalloc_ptr<char> overloaded_name | |
| = compile_cplus_instance::decl_name (TYPE_FN_FIELDLIST_NAME (type, i)); | |
| /* Loop through the fieldlist, adding decls to the compiler's | |
| representation of the class. */ | |
| for (int j = 0; j < TYPE_FN_FIELDLIST_LENGTH (type, i); ++j) | |
| { | |
| /* Skip artificial methods. */ | |
| if (TYPE_FN_FIELD_ARTIFICIAL (methods, j)) | |
| continue; | |
| gcc_cp_symbol_kind_flags sym_kind = GCC_CP_SYMBOL_FUNCTION; | |
| gcc_type method_type; | |
| struct block_symbol sym | |
| = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (methods, j), | |
| instance->block (), VAR_DOMAIN, nullptr); | |
| if (sym.symbol == nullptr) | |
| { | |
| if (TYPE_FN_FIELD_VIRTUAL_P (methods, j)) | |
| { | |
| /* This is beyond hacky, and is really only a workaround for | |
| detecting pure virtual methods. */ | |
| method_type = compile_cplus_convert_method | |
| (instance, type, TYPE_FN_FIELD_TYPE (methods, j)); | |
| instance->plugin ().build_decl | |
| ("pure virtual method", overloaded_name.get (), | |
| (sym_kind | |
| | get_method_access_flag (type, i, j) | |
| | GCC_CP_FLAG_VIRTUAL_FUNCTION | |
| | GCC_CP_FLAG_PURE_VIRTUAL_FUNCTION).raw (), | |
| method_type, nullptr, 0, nullptr, 0); | |
| continue; | |
| } | |
| /* This can happen if we have a DW_AT_declaration DIE | |
| for the method, but no "definition"-type DIE (with | |
| DW_AT_specification referencing the decl DIE), i.e., | |
| the compiler has probably optimized the method away. | |
| In this case, all we can hope to do is issue a warning | |
| to the user letting him know. If the user has not actually | |
| requested using this method, things should still work. */ | |
| warning (_("Method %s appears to be optimized out.\n" | |
| "All references to this method will be undefined."), | |
| TYPE_FN_FIELD_PHYSNAME (methods, j)); | |
| continue; | |
| } | |
| const char *filename = sym.symbol->symtab ()->filename; | |
| unsigned int line = sym.symbol->line (); | |
| CORE_ADDR address = sym.symbol->value_block()->start (); | |
| const char *kind; | |
| if (TYPE_FN_FIELD_STATIC_P (methods, j)) | |
| { | |
| kind = "static method"; | |
| method_type = compile_cplus_convert_func | |
| (instance, TYPE_FN_FIELD_TYPE (methods, j), true); | |
| } | |
| else | |
| { | |
| kind = "method"; | |
| method_type = (compile_cplus_convert_method | |
| (instance, type, TYPE_FN_FIELD_TYPE (methods, j))); | |
| } | |
| if (TYPE_FN_FIELD_VIRTUAL_P (methods, j)) | |
| sym_kind |= GCC_CP_FLAG_VIRTUAL_FUNCTION; | |
| instance->plugin ().build_decl | |
| (kind, overloaded_name.get (), | |
| (sym_kind | get_method_access_flag (type, i, j)).raw (), | |
| method_type, nullptr, address, filename, line); | |
| } | |
| } | |
| } | |
| /* Convert a struct or union type to its gcc representation. If this type | |
| was defined in another type, NESTED_ACCESS should indicate the | |
| accessibility of this type. */ | |
| static gcc_type | |
| compile_cplus_convert_struct_or_union (compile_cplus_instance *instance, | |
| struct type *type, | |
| enum gcc_cp_symbol_kind nested_access) | |
| { | |
| const char *filename = nullptr; | |
| unsigned int line = 0; | |
| /* Get the decl name of this type. */ | |
| gdb::unique_xmalloc_ptr<char> name | |
| = compile_cplus_instance::decl_name (type->name ()); | |
| /* Create a new scope for TYPE. */ | |
| compile_scope scope = instance->new_scope (type->name (), type); | |
| if (scope.nested_type () != GCC_TYPE_NONE) | |
| { | |
| /* The type requested was actually defined inside another type, | |
| such as a nested class definition. Return that type. */ | |
| return scope.nested_type (); | |
| } | |
| /* Push all scopes. */ | |
| instance->enter_scope (std::move (scope)); | |
| /* First we create the resulting type and enter it into our hash | |
| table. This lets recursive types work. */ | |
| gcc_decl resuld; | |
| if (type->code () == TYPE_CODE_STRUCT) | |
| { | |
| const char *what = type->is_declared_class () ? "class" : "struct"; | |
| resuld = instance->plugin ().build_decl | |
| (what, name.get (), (GCC_CP_SYMBOL_CLASS | nested_access | |
| | (type->is_declared_class () | |
| ? GCC_CP_FLAG_CLASS_NOFLAG | |
| : GCC_CP_FLAG_CLASS_IS_STRUCT)), | |
| 0, nullptr, 0, filename, line); | |
| } | |
| else | |
| { | |
| gdb_assert (type->code () == TYPE_CODE_UNION); | |
| resuld = instance->plugin ().build_decl | |
| ("union", name.get (), GCC_CP_SYMBOL_UNION | nested_access, | |
| 0, nullptr, 0, filename, line); | |
| } | |
| gcc_type result; | |
| if (type->code () == TYPE_CODE_STRUCT) | |
| { | |
| int num_baseclasses = TYPE_N_BASECLASSES (type); | |
| std::vector<gcc_type> elements (num_baseclasses); | |
| std::vector<enum gcc_cp_symbol_kind> flags (num_baseclasses); | |
| struct gcc_vbase_array bases {}; | |
| bases.elements = elements.data (); | |
| bases.flags = flags.data (); | |
| bases.n_elements = num_baseclasses; | |
| for (int i = 0; i < num_baseclasses; ++i) | |
| { | |
| struct type *base_type = TYPE_BASECLASS (type, i); | |
| bases.flags[i] = (GCC_CP_SYMBOL_BASECLASS | |
| | get_field_access_flag (type, i) | |
| | (BASETYPE_VIA_VIRTUAL (type, i) | |
| ? GCC_CP_FLAG_BASECLASS_VIRTUAL | |
| : GCC_CP_FLAG_BASECLASS_NOFLAG)); | |
| bases.elements[i] = instance->convert_type (base_type); | |
| } | |
| result = instance->plugin ().start_class_type | |
| (name.get (), resuld, &bases, filename, line); | |
| } | |
| else | |
| { | |
| gdb_assert (type->code () == TYPE_CODE_UNION); | |
| result = instance->plugin ().start_class_type | |
| (name.get (), resuld, nullptr, filename, line); | |
| } | |
| instance->insert_type (type, result); | |
| /* Add definitions. */ | |
| compile_cplus_convert_type_defns (instance, type); | |
| /* Add methods. */ | |
| compile_cplus_convert_struct_or_union_methods (instance, type, result); | |
| /* Add members. */ | |
| compile_cplus_convert_struct_or_union_members (instance, type, result); | |
| /* All finished. */ | |
| instance->plugin ().finish_class_type (name.get (), type->length ()); | |
| /* Pop all scopes. */ | |
| instance->leave_scope (); | |
| return result; | |
| } | |
| /* Convert an enum type to its gcc representation. If this type | |
| was defined in another type, NESTED_ACCESS should indicate the | |
| accessibility of this type.*/ | |
| static gcc_type | |
| compile_cplus_convert_enum (compile_cplus_instance *instance, struct type *type, | |
| enum gcc_cp_symbol_kind nested_access) | |
| { | |
| bool scoped_enum_p = false; | |
| /* Create a new scope for this type. */ | |
| compile_scope scope = instance->new_scope (type->name (), type); | |
| if (scope.nested_type () != GCC_TYPE_NONE) | |
| { | |
| /* The type requested was actually defined inside another type, | |
| such as a nested class definition. Return that type. */ | |
| return scope.nested_type (); | |
| } | |
| gdb::unique_xmalloc_ptr<char> name | |
| = compile_cplus_instance::decl_name (type->name ()); | |
| /* Push all scopes. */ | |
| instance->enter_scope (std::move (scope)); | |
| gcc_type int_type | |
| = instance->plugin ().get_int_type (type->is_unsigned (), | |
| type->length (), nullptr); | |
| gcc_type result | |
| = instance->plugin ().start_enum_type (name.get (), int_type, | |
| GCC_CP_SYMBOL_ENUM | nested_access | |
| | (scoped_enum_p | |
| ? GCC_CP_FLAG_ENUM_SCOPED | |
| : GCC_CP_FLAG_ENUM_NOFLAG), | |
| nullptr, 0); | |
| for (int i = 0; i < type->num_fields (); ++i) | |
| { | |
| gdb::unique_xmalloc_ptr<char> fname | |
| = compile_cplus_instance::decl_name (type->field (i).name ()); | |
| if (type->field (i).loc_kind () != FIELD_LOC_KIND_ENUMVAL | |
| || fname == nullptr) | |
| continue; | |
| instance->plugin ().build_enum_constant (result, fname.get (), | |
| type->field (i).loc_enumval ()); | |
| } | |
| /* Finish enum definition and pop scopes. */ | |
| instance->plugin ().finish_enum_type (result); | |
| instance->leave_scope (); | |
| return result; | |
| } | |
| /* Convert a function type to its gcc representation. This function does | |
| not deal with function templates. */ | |
| static gcc_type | |
| compile_cplus_convert_func (compile_cplus_instance *instance, | |
| struct type *type, bool strip_artificial) | |
| { | |
| int is_varargs = type->has_varargs (); | |
| struct type *target_type = type->target_type (); | |
| /* Functions with no debug info have no return type. Ideally we'd | |
| want to fallback to the type of the cast just before the | |
| function, like GDB's built-in expression parser, but we don't | |
| have access to that type here. For now, fallback to int, like | |
| GDB's parser used to do. */ | |
| if (target_type == nullptr) | |
| { | |
| target_type = builtin_type (type->arch ())->builtin_int; | |
| warning (_("function has unknown return type; assuming int")); | |
| } | |
| /* This approach means we can't make self-referential function | |
| types. Those are impossible in C, though. */ | |
| gcc_type return_type = instance->convert_type (target_type); | |
| std::vector<gcc_type> elements (type->num_fields ()); | |
| struct gcc_type_array array = { (int) type->num_fields (), elements.data () }; | |
| int artificials = 0; | |
| for (int i = 0; i < type->num_fields (); ++i) | |
| { | |
| if (strip_artificial && TYPE_FIELD_ARTIFICIAL (type, i)) | |
| { | |
| --array.n_elements; | |
| ++artificials; | |
| } | |
| else | |
| { | |
| array.elements[i - artificials] | |
| = instance->convert_type (type->field (i).type ()); | |
| } | |
| } | |
| /* We omit setting the argument types to `void' to be a little flexible | |
| with some minsyms like printf (compile-cplus.exp has examples). */ | |
| gcc_type result = instance->plugin ().build_function_type | |
| (return_type, &array, is_varargs); | |
| return result; | |
| } | |
| /* Convert an integer type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_int (compile_cplus_instance *instance, struct type *type) | |
| { | |
| if (type->has_no_signedness ()) | |
| { | |
| gdb_assert (type->length () == 1); | |
| return instance->plugin ().get_char_type (); | |
| } | |
| return instance->plugin ().get_int_type | |
| (type->is_unsigned (), type->length (), type->name ()); | |
| } | |
| /* Convert a floating-point type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_float (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| return instance->plugin ().get_float_type | |
| (type->length (), type->name ()); | |
| } | |
| /* Convert the 'void' type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_void (compile_cplus_instance *instance, struct type *type) | |
| { | |
| return instance->plugin ().get_void_type (); | |
| } | |
| /* Convert a boolean type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_bool (compile_cplus_instance *instance, struct type *type) | |
| { | |
| return instance->plugin ().get_bool_type (); | |
| } | |
| /* See description in compile-cplus.h. */ | |
| gcc_type | |
| compile_cplus_instance::convert_qualified_base (gcc_type base, | |
| gcc_cp_qualifiers_flags quals) | |
| { | |
| gcc_type result = base; | |
| if (quals != 0) | |
| result = plugin ().build_qualified_type (base, quals.raw ()); | |
| return result; | |
| } | |
| /* See description in compile-cplus.h. */ | |
| static gcc_type | |
| compile_cplus_convert_qualified (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| struct type *unqual = make_unqualified_type (type); | |
| gcc_cp_qualifiers_flags quals = (enum gcc_cp_qualifiers) 0; | |
| gcc_type unqual_converted = instance->convert_type (unqual); | |
| if (TYPE_CONST (type)) | |
| quals |= GCC_CP_QUALIFIER_CONST; | |
| if (TYPE_VOLATILE (type)) | |
| quals |= GCC_CP_QUALIFIER_VOLATILE; | |
| if (TYPE_RESTRICT (type)) | |
| quals |= GCC_CP_QUALIFIER_RESTRICT; | |
| return instance->convert_qualified_base (unqual_converted, quals); | |
| } | |
| /* Convert a complex type to its gcc representation. */ | |
| static gcc_type | |
| compile_cplus_convert_complex (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| gcc_type base = instance->convert_type (type->target_type ()); | |
| return instance->plugin ().build_complex_type (base); | |
| } | |
| /* Convert a namespace of TYPE. */ | |
| static gcc_type | |
| compile_cplus_convert_namespace (compile_cplus_instance *instance, | |
| struct type *type) | |
| { | |
| compile_scope scope = instance->new_scope (type->name (), type); | |
| gdb::unique_xmalloc_ptr<char> name | |
| = compile_cplus_instance::decl_name (type->name ()); | |
| /* Push scope. */ | |
| instance->enter_scope (std::move (scope)); | |
| /* Convert this namespace. */ | |
| instance->plugin ().push_namespace (name.get ()); | |
| instance->plugin ().pop_binding_level (name.get ()); | |
| /* Pop scope. */ | |
| instance->leave_scope (); | |
| /* Namespaces are non-cacheable types. */ | |
| return GCC_TYPE_NONE; | |
| } | |
| /* A helper function which knows how to convert most types from their | |
| gdb representation to the corresponding gcc form. This examines | |
| the TYPE and dispatches to the appropriate conversion function. It | |
| returns the gcc type. | |
| If the type was defined in another type, NESTED_ACCESS should indicate the | |
| accessibility of this type. */ | |
| static gcc_type | |
| convert_type_cplus_basic (compile_cplus_instance *instance, | |
| struct type *type, | |
| enum gcc_cp_symbol_kind nested_access) | |
| { | |
| /* If we are converting a qualified type, first convert the | |
| unqualified type and then apply the qualifiers. */ | |
| if ((type->instance_flags () & (TYPE_INSTANCE_FLAG_CONST | |
| | TYPE_INSTANCE_FLAG_VOLATILE | |
| | TYPE_INSTANCE_FLAG_RESTRICT)) != 0) | |
| return compile_cplus_convert_qualified (instance, type); | |
| switch (type->code ()) | |
| { | |
| case TYPE_CODE_REF: | |
| case TYPE_CODE_RVALUE_REF: | |
| return compile_cplus_convert_reference (instance, type); | |
| case TYPE_CODE_PTR: | |
| return compile_cplus_convert_pointer (instance, type); | |
| case TYPE_CODE_ARRAY: | |
| return compile_cplus_convert_array (instance, type); | |
| case TYPE_CODE_STRUCT: | |
| case TYPE_CODE_UNION: | |
| return | |
| compile_cplus_convert_struct_or_union (instance, type, nested_access); | |
| case TYPE_CODE_ENUM: | |
| return compile_cplus_convert_enum (instance, type, nested_access); | |
| case TYPE_CODE_FUNC: | |
| return compile_cplus_convert_func (instance, type, false); | |
| case TYPE_CODE_METHOD: | |
| return | |
| compile_cplus_convert_method (instance, TYPE_SELF_TYPE (type), type); | |
| case TYPE_CODE_MEMBERPTR: | |
| case TYPE_CODE_METHODPTR: | |
| return compile_cplus_convert_memberptr (instance, type); | |
| break; | |
| case TYPE_CODE_INT: | |
| return compile_cplus_convert_int (instance, type); | |
| case TYPE_CODE_FLT: | |
| return compile_cplus_convert_float (instance, type); | |
| case TYPE_CODE_VOID: | |
| return compile_cplus_convert_void (instance, type); | |
| case TYPE_CODE_BOOL: | |
| return compile_cplus_convert_bool (instance, type); | |
| case TYPE_CODE_COMPLEX: | |
| return compile_cplus_convert_complex (instance, type); | |
| case TYPE_CODE_NAMESPACE: | |
| return compile_cplus_convert_namespace (instance, type); | |
| case TYPE_CODE_TYPEDEF: | |
| return compile_cplus_convert_typedef (instance, type, nested_access); | |
| default: | |
| break; | |
| } | |
| std::string s = string_printf (_("unhandled TYPE_CODE %d"), | |
| type->code ()); | |
| return instance->plugin ().error (s.c_str ()); | |
| } | |
| gcc_type | |
| compile_cplus_instance::convert_type (struct type *type, | |
| enum gcc_cp_symbol_kind nested_access) | |
| { | |
| /* Check if TYPE has already been converted. */ | |
| gcc_type result; | |
| if (get_cached_type (type, &result)) | |
| return result; | |
| /* It is the first time this type has been seen -- convert it | |
| and cache it, if appropriate.. */ | |
| result = convert_type_cplus_basic (this, type, nested_access); | |
| if (result != GCC_TYPE_NONE) | |
| insert_type (type, result); | |
| return result; | |
| } | |
| void | |
| compile_cplus_instance::gcc_cplus_enter_scope | |
| (void *datum, struct gcc_cp_context *gcc_context) | |
| { | |
| } | |
| void | |
| compile_cplus_instance::gcc_cplus_leave_scope | |
| (void *datum, struct gcc_cp_context *gcc_context) | |
| { | |
| } | |
| /* Plug-in forwards. */ | |
| /* C++ plug-in wrapper. */ | |
| /* A result printer for plug-in calls that return a gcc_type or | |
| gcc_decl. */ | |
| static void | |
| compile_cplus_debug_output_1 (ULONGEST arg) | |
| { | |
| gdb_printf (gdb_stdlog, "%s", pulongest (arg)); | |
| } | |
| static void | |
| compile_cplus_debug_output_1 (const char *arg) | |
| { | |
| if (arg == nullptr) | |
| gdb_puts ("NULL", gdb_stdlog); | |
| else | |
| gdb_puts (arg, gdb_stdlog); | |
| } | |
| static void | |
| compile_cplus_debug_output () | |
| { | |
| } | |
| template <typename T> | |
| static void | |
| compile_cplus_debug_output_1 (const T *arg) | |
| { | |
| } | |
| template <typename T, typename... Targs> | |
| static void | |
| compile_cplus_debug_output (T arg, Targs... Args) | |
| { | |
| compile_cplus_debug_output_1 (arg); | |
| gdb_putc (' ', gdb_stdlog); | |
| compile_cplus_debug_output (Args...); | |
| } | |
| #define FORWARD(OP,...) m_context->cp_ops->OP(m_context, ##__VA_ARGS__) | |
| #define OUTPUT_DEBUG_RESULT(R) \ | |
| if (debug_compile_cplus_types) \ | |
| { \ | |
| gdb_puts (": ", gdb_stdlog); \ | |
| compile_cplus_debug_output (R); \ | |
| gdb_putc ('\n', gdb_stdlog); \ | |
| } \ | |
| #define GCC_METHOD0(R, N) \ | |
| R gcc_cp_plugin::N () const \ | |
| { \ | |
| if (debug_compile_cplus_types) \ | |
| compile_cplus_debug_output (STRINGIFY (N)); \ | |
| auto result = FORWARD (N); \ | |
| OUTPUT_DEBUG_RESULT (result); \ | |
| return result; \ | |
| } | |
| #define GCC_METHOD1(R, N, A) \ | |
| R gcc_cp_plugin::N (A a) const \ | |
| { \ | |
| if (debug_compile_cplus_types) \ | |
| compile_cplus_debug_output (STRINGIFY (N), a); \ | |
| auto result = FORWARD (N, a); \ | |
| OUTPUT_DEBUG_RESULT (result); \ | |
| return result; \ | |
| } | |
| #define GCC_METHOD2(R, N, A, B) \ | |
| R gcc_cp_plugin::N (A a, B b) const \ | |
| { \ | |
| if (debug_compile_cplus_types) \ | |
| compile_cplus_debug_output (STRINGIFY (N), a, b); \ | |
| auto result = FORWARD (N, a, b); \ | |
| OUTPUT_DEBUG_RESULT (result); \ | |
| return result; \ | |
| } | |
| #define GCC_METHOD3(R, N, A, B, C) \ | |
| R gcc_cp_plugin::N (A a, B b, C c) const \ | |
| { \ | |
| if (debug_compile_cplus_types) \ | |
| compile_cplus_debug_output (STRINGIFY (N), a, b, c); \ | |
| auto result = FORWARD (N, a, b, c); \ | |
| OUTPUT_DEBUG_RESULT (result); \ | |
| return result; \ | |
| } | |
| #define GCC_METHOD4(R, N, A, B, C, D) \ | |
| R gcc_cp_plugin::N (A a, B b, C c, D d) const \ | |
| { \ | |
| if (debug_compile_cplus_types) \ | |
| compile_cplus_debug_output (STRINGIFY (N), a, b, c, d); \ | |
| auto result = FORWARD (N, a, b, c, d); \ | |
| OUTPUT_DEBUG_RESULT (result); \ | |
| return result; \ | |
| } | |
| #define GCC_METHOD5(R, N, A, B, C, D, E) \ | |
| R gcc_cp_plugin::N (A a, B b, C c, D d, E e) const \ | |
| { \ | |
| if (debug_compile_cplus_types) \ | |
| compile_cplus_debug_output (STRINGIFY (N), a, b, c, d, e); \ | |
| auto result = FORWARD (N, a, b, c, d, e); \ | |
| OUTPUT_DEBUG_RESULT (result); \ | |
| return result; \ | |
| } | |
| #define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ | |
| R gcc_cp_plugin::N (A a, B b, C c, D d, E e, F f, G g) const \ | |
| { \ | |
| if (debug_compile_cplus_types) \ | |
| compile_cplus_debug_output (STRINGIFY (N), a, b, c, d, e, f, g); \ | |
| auto result = FORWARD (N, a, b, c, d, e, f, g); \ | |
| OUTPUT_DEBUG_RESULT (result); \ | |
| return result; \ | |
| } | |
| #include "gcc-cp-fe.def" | |
| #undef GCC_METHOD0 | |
| #undef GCC_METHOD1 | |
| #undef GCC_METHOD2 | |
| #undef GCC_METHOD3 | |
| #undef GCC_METHOD4 | |
| #undef GCC_METHOD5 | |
| #undef GCC_METHOD7 | |
| #undef FORWARD | |
| #undef OUTPUT_DEBUG_RESULT | |
| gcc_expr | |
| gcc_cp_plugin::build_decl (const char *debug_decltype, const char *name, | |
| enum gcc_cp_symbol_kind sym_kind, gcc_type sym_type, | |
| const char *substitution_name, gcc_address address, | |
| const char *filename, unsigned int line_number) | |
| { | |
| if (debug_compile_cplus_types) | |
| gdb_printf (gdb_stdlog, "<%s> ", debug_decltype); | |
| return build_decl (name, sym_kind, sym_type, substitution_name, | |
| address, filename, line_number); | |
| } | |
| gcc_type | |
| gcc_cp_plugin::start_class_type (const char *debug_name, gcc_decl typedecl, | |
| const struct gcc_vbase_array *base_classes, | |
| const char *filename, unsigned int line_number) | |
| { | |
| if (debug_compile_cplus_types) | |
| gdb_printf (gdb_stdlog, "<%s> ", debug_name); | |
| return start_class_type (typedecl, base_classes, filename, line_number); | |
| } | |
| int | |
| gcc_cp_plugin::finish_class_type (const char *debug_name, | |
| unsigned long size_in_bytes) | |
| { | |
| if (debug_compile_cplus_types) | |
| gdb_printf (gdb_stdlog, "<%s> ", debug_name); | |
| return finish_class_type (size_in_bytes); | |
| } | |
| int | |
| gcc_cp_plugin::pop_binding_level (const char *debug_name) | |
| { | |
| if (debug_compile_cplus_types) | |
| gdb_printf (gdb_stdlog, "<%s> ", debug_name); | |
| return pop_binding_level (); | |
| } | |
| void _initialize_compile_cplus_types (); | |
| void | |
| _initialize_compile_cplus_types () | |
| { | |
| add_setshow_boolean_cmd ("compile-cplus-types", no_class, | |
| &debug_compile_cplus_types, _("\ | |
| Set debugging of C++ compile type conversion."), _("\ | |
| Show debugging of C++ compile type conversion."), _("\ | |
| When enabled debugging messages are printed during C++ type conversion for\n\ | |
| the compile commands."), | |
| nullptr, | |
| nullptr, | |
| &setdebuglist, | |
| &showdebuglist); | |
| add_setshow_boolean_cmd ("compile-cplus-scopes", no_class, | |
| &debug_compile_cplus_scopes, _("\ | |
| Set debugging of C++ compile scopes."), _("\ | |
| Show debugging of C++ compile scopes."), _("\ | |
| When enabled debugging messages are printed about definition scopes during\n\ | |
| C++ type conversion for the compile commands."), | |
| nullptr, | |
| nullptr, | |
| &setdebuglist, | |
| &showdebuglist); | |
| } |